Thoth
A functional, expressive, asynchronous C++26 webdev library
Loading...
Searching...
No Matches
_base.hpp
Go to the documentation of this file.
1#pragma once
2#include <string_view>
3#include <optional>
4#include <variant>
5#include <iomanip>
6#include <format>
7#include <chrono>
8#include <string>
9
12
13namespace Thoth::Http::NHeaders {
14 template<class T>
15 struct Scanner {
16 Scanner() = delete;
17 Scanner(Scanner&) = delete;
19 };
20
21 template<class T>
22 std::optional<T> Scan(std::string_view input, std::string_view args = "") {
23 Scanner<T> scanner{};
24
25 if (!scanner.Parse(args))
26 return std::nullopt;
27
28 return scanner.Scan(input);
29 }
30 // TODO: Implement Scan in a better way.
31
32
33 template<class T>
34 concept Scannable = requires (Scanner<std::remove_cvref_t<T>> scanner, std::string_view str) {
35 { scanner.Parse(str) } -> std::same_as<bool>;
36 { scanner.Scan(str) } -> std::same_as<std::optional<std::remove_cvref_t<T>>>;
37 };
38
39 template<class T>
40 concept Serializable = Scannable<T> && std::formattable<T, char>;
41
42
43 enum class HeaderErrorEnum {
47 };
49
50
51
52
53
54
55 template<std::integral T>
56 struct Scanner<T> {
57 private:
58 int base{ 10 };
59 public:
60 bool Parse(const std::string_view str) {
61 if (str.empty()) return true;
62 if (str == "b") { base = 2; return true; }
63 if (str == "x") { base = 16; return true; }
64
65 const auto [_, ec]{ std::from_chars(str.data(), str.data() + str.size(), base) };
66
67
68 return ec == std::errc() && base >= 1 && base <= 36;
69 }
70 std::optional<T> Scan(const std::string_view str) {
71 T val;
72 const auto [_, ec]{ std::from_chars(str.data(), str.data() + str.size(), val, base) };
73
74 if (ec == std::errc())
75 return val;
76
77 return std::nullopt;
78 }
79 };
80
81 static_assert(Serializable<int>);
82
83 template<>
84 struct Scanner<std::string> {
85 static bool Parse(const std::string_view str) {
86 return str.empty();
87 }
88
89 static std::optional<std::string> Scan(std::string_view input) {
90 return String::TrimmedStr(input);
91 }
92 };
93
94 static_assert(Serializable<std::string>);
95
96 template<>
97 struct Scanner<std::chrono::utc_clock::time_point> {
98 std::string pattern{ "%a, %d %b %Y %H:%M:%S GMT" };
99
100 static bool Parse(const std::string_view str) {
101 if (str.empty()) return true;
102
103 std::tm tm{};
104 std::istringstream ss{ std::string{ str } };
105 ss >> std::get_time(&tm, "%a, %d %b %Y %H:%M:%S GMT");
106 return !ss.fail();
107 }
108
109 static std::optional<std::chrono::utc_clock::time_point> Scan(std::string_view input) {
110 using TimePoint = std::chrono::utc_clock::time_point;
111 static constexpr char outputFormat[]{ "%Y-%m-%d %H:%M:%S %z" };
112
113 if (input.empty()) return std::nullopt;
114
115 std::istringstream ss{ std::string{ input } };
116
117
118 if (TimePoint clock; std::chrono::from_stream(ss, outputFormat, clock))
119 return clock;
120
121 return std::nullopt;
122 }
123 };
124
125 static_assert(Serializable<std::chrono::utc_clock::time_point>);
126
127 template<class Ped, class Ratio>
128 struct Scanner<std::chrono::duration<Ped, Ratio>> {
129 using Duration = std::chrono::duration<Ped, Ratio>;
130
131 static bool Parse(const std::string_view str) {
132 return str.empty();
133 }
134
135 static std::optional<Duration> Scan(std::string_view input) {
136 if (input.empty()) return std::nullopt;
137
138 if (const auto timeAsNumber{ NHeaders::Scan<Ped>(input) }; timeAsNumber)
139 return { *timeAsNumber };
140
141 return std::nullopt;
142 }
143 };
144
145
146
147 // std::format("{}", dur) will print with a type suffix (like 350min or 180s). It must be "{:Q}".
148 // TODO: creates a pattern to output too.
149 static_assert(Serializable<std::chrono::seconds>);
150
151
152 template<>
153 struct Scanner<std::monostate> {
154 static bool Parse(const std::string_view str) {
155 return str.empty();
156 }
157
158 static std::optional<std::monostate> Scan(std::string_view input) {
159 if (String::TrimmedStr(input) == "null")
160 return std::monostate{};
161 return std::nullopt;
162 }
163 };
164
165 static_assert(Serializable<std::monostate>);
166}
Definition _base.hpp:34
Definition _base.hpp:5
std::optional< T > Scan(std::string_view input, std::string_view args="")
Definition _base.hpp:22
HeaderErrorEnum
Definition _base.hpp:43
std::string string
Definition Url.hpp:8
std::string TrimmedStr(std::string_view str, std::string_view trim=CharSequences::whitespace)
std::optional< T > Scan(const std::string_view str)
Definition _base.hpp:70
bool Parse(const std::string_view str)
Definition _base.hpp:60
std::chrono::duration< Ped, Ratio > Duration
Definition _base.hpp:129
static std::optional< Duration > Scan(std::string_view input)
Definition _base.hpp:135
static bool Parse(const std::string_view str)
Definition _base.hpp:131
static std::optional< std::chrono::utc_clock::time_point > Scan(std::string_view input)
Definition _base.hpp:109
static bool Parse(const std::string_view str)
Definition _base.hpp:100
static bool Parse(const std::string_view str)
Definition _base.hpp:154
static std::optional< std::monostate > Scan(std::string_view input)
Definition _base.hpp:158
static bool Parse(const std::string_view str)
Definition _base.hpp:85
static std::optional< std::string > Scan(std::string_view input)
Definition _base.hpp:89
Definition _base.hpp:15
Scanner & operator=(Scanner &)=delete