Skip to content

Commit 6bb569d

Browse files
committed
spec walk subobjects, start json5 serialization customization point
1 parent 8a73c20 commit 6bb569d

File tree

6 files changed

+113
-11
lines changed

6 files changed

+113
-11
lines changed

include/rsl/cli/spec.hpp

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
namespace rsl {
1616
struct cli;
1717
struct config;
18-
}
18+
} // namespace rsl
1919

2020
namespace rsl::_cli_impl {
2121

@@ -35,8 +35,12 @@ struct Spec {
3535
// but cannot be used in combination with variadic or optional arguments
3636
std::vector<Spec> subcommands;
3737

38+
bool allow_positional;
39+
40+
consteval explicit Parser(bool allow_positional = true) : allow_positional(allow_positional) {}
41+
3842
consteval void parse(std::meta::info r) {
39-
auto ctx = std::meta::access_context::current();
43+
auto ctx = std::meta::access_context::current();
4044
for (auto base : bases_of(r, ctx)) {
4145
parse_base(r, base);
4246
}
@@ -77,12 +81,30 @@ struct Spec {
7781
return false;
7882
}
7983

80-
if (!meta::has_annotation(r, ^^annotations::Positional)) {
81-
return false;
84+
if (meta::has_annotation(r, ^^annotations::Positional)) {
85+
if (!allow_positional) {
86+
compile_error("Positional arguments are only supported at the config root.");
87+
}
88+
arguments.emplace_back(idx, r);
89+
return true;
8290
}
8391

84-
arguments.emplace_back(idx, r);
85-
return true;
92+
if (extract<bool>(substitute(^^std::derived_from, {type_of(r), ^^config}))) {
93+
auto name = identifier_of(r);
94+
95+
auto subparser = Parser(false);
96+
subparser.parse(type_of(r));
97+
98+
for (auto option : subparser.options) {
99+
option.name = std::define_static_string(std::string(name) + ':' + option.name);
100+
options.push_back(option);
101+
}
102+
for (auto command : subparser.commands) {
103+
command.name = std::define_static_string(std::string(name) + ':' + command.name);
104+
options.push_back(command);
105+
}
106+
}
107+
return false;
86108
}
87109

88110
consteval bool parse_command(std::meta::info r) {

include/rsl/json5/json5.hpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
#include <rsl/_impl/default_construct.hpp>
99

1010
namespace rsl::json5 {
11-
struct Parser;
12-
struct Value;
11+
template <typename T>
12+
struct Serializer;
1313

14+
struct Value;
15+
template <typename T>
16+
using Tag = std::type_identity<T>;
1417
using Object = std::unordered_map<std::string, Value>;
1518
using Array = std::vector<Value>;
1619

@@ -31,8 +34,9 @@ struct Value {
3134
throw std::runtime_error("unimplemented");
3235
} else if constexpr (std::is_aggregate_v<T>) {
3336
return as_aggregate<T>();
37+
} else {
38+
return Serializer<T>::deserialize(*this);
3439
}
35-
throw std::runtime_error("unimplemented");
3640
}
3741

3842
template <typename T>
@@ -98,4 +102,15 @@ struct Parser {
98102

99103
Value load(std::string_view path);
100104

105+
template <typename T>
106+
Value from(T const& obj) {
107+
return Serializer<T>::serialize(obj);
108+
}
109+
110+
template <typename T>
111+
T to(Value const& json) {
112+
return Serializer<T>::deserialize(json);
113+
}
114+
115+
101116
} // namespace rsl::json5

include/rsl/json5/serializer.hpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include "json5.hpp"
2+
3+
namespace rsl::json5 {
4+
template <typename T>
5+
struct Serializer {
6+
static T deserialize(Value const& json)
7+
requires requires { {from_json(Tag<T>{}, json)} -> std::convertible_to<T>;}
8+
{
9+
return from_json(Tag<T>{}, json);
10+
}
11+
12+
static Value serialize(T const& obj)
13+
requires requires { {to_json(Tag<T>{}, obj)} -> std::convertible_to<Value>;}
14+
{
15+
return to_json(Tag<T>{}, obj);
16+
}
17+
};
18+
19+
template <>
20+
struct Serializer<bool> {
21+
static bool deserialize(Value const& json);
22+
static Value serialize(bool obj);
23+
};
24+
25+
template <>
26+
struct Serializer<std::string> {
27+
static std::string deserialize(Value const& json);
28+
static Value serialize(std::string obj);
29+
};
30+
31+
template <typename T>
32+
requires std::integral<T> || std::floating_point<T>
33+
struct Serializer<T> {
34+
static T deserialize(Value const& json) {
35+
return json.template as_number<T>();
36+
}
37+
38+
static Value serialize(T obj) {
39+
throw "unimplemented";
40+
}
41+
};
42+
}

src/json5/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
target_sources(rsl_config PRIVATE
22
parser.cpp
3+
serializer.cpp
34
)

src/json5/parser.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,9 @@ Array Value::as_array() const {
191191
}
192192

193193
bool Value::as_bool() const {
194-
if (raw == "true")
194+
if (raw == "true" || raw == "1")
195195
return true;
196-
if (raw == "false")
196+
if (raw == "false" || raw == "0")
197197
return false;
198198
throw std::runtime_error("Could not extract as bool");
199199
}

src/json5/serializer.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <rsl/json5/serializer.hpp>
2+
3+
namespace rsl::json5 {
4+
bool Serializer<bool>::deserialize(Value const& json) {
5+
return json.as_bool();
6+
}
7+
8+
Value Serializer<bool>::serialize(bool obj) {
9+
throw "unimplemented";
10+
}
11+
12+
std::string Serializer<std::string>::deserialize(Value const& json) {
13+
std::string content;
14+
if (!Parser(json.raw).try_string(content)) {
15+
throw std::runtime_error("Could not extract as string.");
16+
}
17+
return content;
18+
}
19+
Value Serializer<std::string>::serialize(std::string obj) {
20+
throw "unimplemented";
21+
}
22+
}

0 commit comments

Comments
 (0)