summaryrefslogtreecommitdiff
path: root/src/util.hpp
blob: 8f2a585c770d21f15c14f772ed085697b358161b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#pragma once

#include <chrono>
#include <command.h>
#include <scn/scan.h>
#include <tuple>

namespace WebStat {
	template<auto Deleter> struct DeleteWith {
		auto
		operator()(auto obj)
		{
			return Deleter(obj);
		}
	};

	template<typename... T>
	auto
	visit(auto && visitor, const std::tuple<T...> & values)
	{
		std::apply(
				[&](auto &&... value) {
					(visitor(value), ...);
				},
				values);
	}

	using FilePtr = std::unique_ptr<std::FILE, DeleteWith<&fclose>>;

	template<typename... T>
	void
	bindMany(const DB::CommandPtr & cmd, unsigned int firstParam, T &&... param)
	{
		(cmd->bindParam(firstParam++, std::forward<T>(param)), ...);
	}

	namespace detail {
		template<typename Rep, typename Period, typename AddPeriod>
		void
		add(std::chrono::duration<Rep, Period> & subtotal, Rep value)
		{
			if constexpr (requires { subtotal += AddPeriod {value}; }) {
				subtotal += AddPeriod {value};
			}
		}
	}

	template<typename Rep, typename Period>
	std::chrono::duration<Rep, Period>
	parseDuration(std::string_view input)
	{
		static constexpr std::initializer_list<
				std::pair<void (*)(std::chrono::duration<Rep, Period> & subtotal, Rep value), std::string_view>>
				DURATION_UNITS {
						{detail::add<Rep, Period, std::chrono::milliseconds>, "ms"},
						{detail::add<Rep, Period, std::chrono::seconds>, "s"},
						{detail::add<Rep, Period, std::chrono::minutes>, "m"},
						{detail::add<Rep, Period, std::chrono::hours>, "h"},
						{detail::add<Rep, Period, std::chrono::days>, "d"},
						{detail::add<Rep, Period, std::chrono::weeks>, "w"},
				};

		std::chrono::duration<Rep, Period> out {};
		auto inputSubRange = scn::ranges::subrange {input};

		while (auto result = scn::scan<Rep, std::string>(inputSubRange, "{}{:[a-z]}")) {
			const auto & [count, chars] = result->values();
			if (auto unit = std::ranges::find(
						DURATION_UNITS, chars, &std::decay_t<decltype(*DURATION_UNITS.begin())>::second);
					unit != DURATION_UNITS.end()) {
				unit->first(out, count);
			}
			inputSubRange = result->range();
		}

		return out;
	}
}