Skip to content

Commit 50123aa

Browse files
committed
Language/JSON
1 parent 6bb30f0 commit 50123aa

8 files changed

Lines changed: 724 additions & 4 deletions

File tree

modules/Language/JSON/Mapping.mpp

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
export module CppUtils.Language.JSON.Mapping;
2+
3+
import std;
4+
5+
import CppUtils.String;
6+
import CppUtils.Type.Mapping;
7+
8+
export namespace CppUtils::Language::JSON
9+
{
10+
struct Value;
11+
12+
using Object = std::map<std::string, Value, std::less<>>;
13+
using Array = std::vector<Value>;
14+
15+
template<std::output_iterator<char> Iterator>
16+
[[nodiscard]] inline auto escapeString(std::string_view string, Iterator out) -> Iterator
17+
{
18+
using namespace std::literals;
19+
using EscapeMapping = Type::Mapping<
20+
Type::Pair<'"', String::Hasher{R"(\")"}>,
21+
Type::Pair<'\\', String::Hasher{R"(\)"}>,
22+
Type::Pair<'\b', String::Hasher{R"(\b)"}>,
23+
Type::Pair<'\f', String::Hasher{R"(\f)"}>,
24+
Type::Pair<'\n', String::Hasher{R"(\n)"}>,
25+
Type::Pair<'\r', String::Hasher{R"(\r)"}>,
26+
Type::Pair<'\t', String::Hasher{R"(\t)"}>>;
27+
28+
*out++ = '"';
29+
for (const auto character : string)
30+
if (const auto escaped = EscapeMapping::toRhs(character, std::string_view{}); not std::empty(escaped))
31+
out = std::ranges::copy(escaped, out).out;
32+
else if (static_cast<unsigned char>(character) < 0x20)
33+
out = std::format_to(out, R"(\u{:04x})", static_cast<int>(character));
34+
else
35+
*out++ = character;
36+
*out++ = '"';
37+
return out;
38+
}
39+
40+
struct Value final
41+
{
42+
std::variant<std::monostate, bool, double, std::string, Array, Object> data;
43+
44+
constexpr Value() = default;
45+
constexpr Value(bool value): data{value} {}
46+
constexpr Value(double value): data{value} {}
47+
inline Value(std::string value): data{std::move(value)} {}
48+
inline Value(const char* value): data{std::string{value}} {}
49+
inline Value(Array value): data{std::move(value)} {}
50+
inline Value(Object value): data{std::move(value)} {}
51+
52+
[[nodiscard]] constexpr auto isNull() const -> bool { return std::holds_alternative<std::monostate>(data); }
53+
[[nodiscard]] constexpr auto isBool() const -> bool { return std::holds_alternative<bool>(data); }
54+
[[nodiscard]] constexpr auto isNumber() const -> bool { return std::holds_alternative<double>(data); }
55+
[[nodiscard]] constexpr auto isString() const -> bool { return std::holds_alternative<std::string>(data); }
56+
[[nodiscard]] constexpr auto isArray() const -> bool { return std::holds_alternative<Array>(data); }
57+
[[nodiscard]] constexpr auto isObject() const -> bool { return std::holds_alternative<Object>(data); }
58+
59+
template<class T>
60+
[[nodiscard]] constexpr auto is() const -> bool
61+
{
62+
return std::holds_alternative<T>(data);
63+
}
64+
65+
template<class T>
66+
[[nodiscard]] constexpr auto&& as(this auto&& self)
67+
{
68+
return std::get<T>(std::forward<decltype(self)>(self).data);
69+
}
70+
71+
template<class T>
72+
[[nodiscard]] inline auto getIf() const -> std::optional<std::reference_wrapper<const T>>
73+
{
74+
if (const auto* value = std::get_if<T>(std::addressof(data)))
75+
return std::ref(*value);
76+
return std::nullopt;
77+
}
78+
79+
[[nodiscard]] inline auto operator[](std::string_view key) const -> const Value&;
80+
[[nodiscard]] inline auto operator[](std::size_t index) const -> const Value&;
81+
82+
[[nodiscard]] constexpr auto operator==(const Value&) const -> bool = default;
83+
84+
[[nodiscard]] inline operator std::string() const
85+
{
86+
auto result = std::string{};
87+
dump(std::back_inserter(result));
88+
return result;
89+
}
90+
91+
template<std::output_iterator<char> Iterator>
92+
inline auto dump(Iterator out) const -> Iterator
93+
{
94+
using namespace std::literals;
95+
return std::visit([&](const auto& value) -> Iterator {
96+
using T = std::remove_cvref_t<decltype(value)>;
97+
if constexpr (std::same_as<T, std::monostate>)
98+
return std::ranges::copy("null"sv, out).out;
99+
if constexpr (std::same_as<T, bool>)
100+
return std::ranges::copy(value ? "true"sv : "false"sv, out).out;
101+
if constexpr (std::same_as<T, double>)
102+
return std::ranges::copy(String::formatValue(value), out).out;
103+
if constexpr (std::same_as<T, std::string>)
104+
return escapeString(value, out);
105+
if constexpr (std::same_as<T, Array>)
106+
{
107+
*out++ = '[';
108+
for (auto first = true; const auto& item : value)
109+
{
110+
if (not first)
111+
*out++ = ',';
112+
out = item.dump(out);
113+
first = false;
114+
}
115+
*out++ = ']';
116+
return out;
117+
}
118+
if constexpr (std::same_as<T, Object>)
119+
{
120+
*out++ = '{';
121+
for (auto first = true; const auto& [key, val] : value)
122+
{
123+
if (not first)
124+
*out++ = ',';
125+
out = escapeString(key, out);
126+
*out++ = ':';
127+
out = val.dump(out);
128+
first = false;
129+
}
130+
*out++ = '}';
131+
return out;
132+
}
133+
std::unreachable();
134+
}, data);
135+
}
136+
};
137+
138+
inline constinit const auto nullValue = Value{};
139+
140+
[[nodiscard]] inline auto Value::operator[](std::string_view key) const -> const Value&
141+
{
142+
if (auto object = getIf<Object>())
143+
{
144+
const auto& objectRef = object->get();
145+
if (auto it = objectRef.find(key); it != std::end(objectRef))
146+
return it->second;
147+
}
148+
return nullValue;
149+
}
150+
151+
[[nodiscard]] inline auto Value::operator[](std::size_t index) const -> const Value&
152+
{
153+
if (auto array = getIf<Array>())
154+
{
155+
const auto& arrayRef = array->get();
156+
if (index < std::size(arrayRef))
157+
return arrayRef[index];
158+
}
159+
return nullValue;
160+
}
161+
162+
inline auto operator<<(std::ostream& os, const Value& value) -> std::ostream&
163+
{
164+
return os << static_cast<std::string>(value);
165+
}
166+
167+
template<class ObjectType, class Mapping>
168+
requires (std::is_default_constructible_v<ObjectType>)
169+
[[nodiscard]] inline auto toStruct(const Value& jsonValue) -> ObjectType
170+
{
171+
using JSONMapping = typename Mapping::JSONMapping;
172+
using StructMapping = typename Mapping::StructMapping;
173+
using FunctionMapping = typename Mapping::FunctionMapping;
174+
175+
auto objectValue = ObjectType{};
176+
std::apply([&](auto&&... jsonPairs) -> void {
177+
([&](auto&& jsonPair) -> void {
178+
using JSONPair = std::remove_cvref_t<decltype(jsonPair)>;
179+
constexpr auto token = JSONPair::lhs;
180+
constexpr auto jsonKeyValue = std::get<0>(JSONPair::rhs);
181+
auto getKeyString = [&]() -> std::string {
182+
using KeyType = std::remove_cvref_t<decltype(jsonKeyValue)>;
183+
if constexpr (std::convertible_to<KeyType, std::string_view>)
184+
return static_cast<std::string>(static_cast<std::string_view>(jsonKeyValue));
185+
else if constexpr (std::convertible_to<KeyType, std::string>)
186+
return static_cast<std::string>(jsonKeyValue);
187+
else
188+
return {};
189+
};
190+
const auto& value = jsonValue[getKeyString()];
191+
if (not value.isNull())
192+
{
193+
constexpr auto memberPointer = std::get<0>(StructMapping::template toRhs<token>());
194+
auto& member = objectValue.*memberPointer;
195+
using MemberType = std::remove_cvref_t<decltype(member)>;
196+
if constexpr (FunctionMapping::template containsLhs<token>())
197+
FunctionMapping::template toSingleRhs<token>()(member, value);
198+
else
199+
std::visit([&](const auto& value) {
200+
using ValueType = std::remove_cvref_t<decltype(value)>;
201+
if constexpr (std::same_as<ValueType, std::monostate>)
202+
return;
203+
if constexpr (std::same_as<MemberType, bool>)
204+
{
205+
if constexpr (std::same_as<ValueType, bool>)
206+
member = value;
207+
}
208+
else if constexpr (std::same_as<MemberType, std::string_view> or std::same_as<MemberType, std::string>)
209+
{
210+
if constexpr (std::same_as<ValueType, std::string>)
211+
member = value;
212+
}
213+
else if constexpr (std::integral<MemberType> or std::floating_point<MemberType>)
214+
{
215+
if constexpr (std::same_as<ValueType, double> or std::same_as<ValueType, bool>)
216+
member = static_cast<MemberType>(value);
217+
}
218+
}, value.data);
219+
}
220+
}(jsonPairs), ...);
221+
}, typename JSONMapping::Values{});
222+
return objectValue;
223+
}
224+
225+
template<class ObjectType, class Mapping>
226+
[[nodiscard]] inline auto toStructs(
227+
const Value& jsonValue,
228+
std::vector<ObjectType>&& objects = {}) -> std::vector<ObjectType>
229+
{
230+
if (jsonValue.isArray())
231+
for (const auto& item : jsonValue.as<Array>())
232+
objects.push_back(toStruct<ObjectType, Mapping>(item));
233+
return std::move(objects);
234+
}
235+
236+
template<class ObjectType, class Mapping>
237+
requires (std::is_default_constructible_v<ObjectType>)
238+
[[nodiscard]] inline auto toJSON(const ObjectType& objectValue) -> Value
239+
{
240+
using JSONMapping = typename Mapping::JSONMapping;
241+
using StructMapping = typename Mapping::StructMapping;
242+
243+
auto object = Object{};
244+
std::apply([&](auto&&... jsonPairs) -> void {
245+
([&](auto&& jsonPair) -> void {
246+
using JSONPair = std::remove_cvref_t<decltype(jsonPair)>;
247+
constexpr auto token = JSONPair::lhs;
248+
constexpr auto jsonKeyValue = std::get<0>(JSONPair::rhs);
249+
auto getKeyString = [&]() -> std::string {
250+
using KeyType = std::remove_cvref_t<decltype(jsonKeyValue)>;
251+
if constexpr (std::convertible_to<KeyType, std::string_view>)
252+
return static_cast<std::string>(static_cast<std::string_view>(jsonKeyValue));
253+
else if constexpr (std::convertible_to<KeyType, std::string>)
254+
return static_cast<std::string>(jsonKeyValue);
255+
else
256+
return {};
257+
};
258+
const auto jsonKey = getKeyString();
259+
constexpr auto memberPointer = std::get<0>(StructMapping::template toRhs<token>());
260+
const auto& member = objectValue.*memberPointer;
261+
using MemberType = std::remove_cvref_t<decltype(member)>;
262+
if constexpr (std::same_as<MemberType, bool>)
263+
object[jsonKey] = Value{member};
264+
else if constexpr (std::same_as<MemberType, std::string_view>)
265+
object[jsonKey] = Value{std::string{member}};
266+
else if constexpr (std::same_as<MemberType, std::string>)
267+
object[jsonKey] = Value{member};
268+
else if constexpr (std::integral<MemberType> or std::floating_point<MemberType>)
269+
object[jsonKey] = Value{static_cast<double>(member)};
270+
}(jsonPairs), ...);
271+
}, typename JSONMapping::Values{});
272+
return Value{object};
273+
}
274+
}
275+
276+
template<>
277+
struct std::formatter<CppUtils::Language::JSON::Value>: std::formatter<std::string>
278+
{
279+
inline auto format(const CppUtils::Language::JSON::Value& value, std::format_context& context) const
280+
{
281+
return value.dump(context.out());
282+
}
283+
};

0 commit comments

Comments
 (0)