Skip to content

Commit d58178f

Browse files
committed
repr: init
1 parent 03cbb92 commit d58178f

9 files changed

Lines changed: 147 additions & 24 deletions

File tree

example/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ function(DEFINE_EXAMPLE TARGET)
55
target_link_libraries(example_${TARGET} PRIVATE rsl_serialize)
66
endfunction()
77

8-
DEFINE_EXAMPLE(xml)
8+
DEFINE_EXAMPLE(xml)
9+
DEFINE_EXAMPLE(repr)

example/repr.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <print>
2+
#include <rsl/repr>
3+
4+
struct Test {
5+
int foo = 3;
6+
std::vector<int> bar {1, 5, 8, 29, 3};
7+
};
8+
9+
int main() {
10+
auto obj = Test();
11+
std::println("{}", rsl::repr(obj));
12+
}

include/rsl/repr

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <string>
44

55
#include <meta>
6+
#include <rsl/serializer/repr/serializer.hpp>
67

78
namespace rsl {
89
template <auto V>
@@ -12,12 +13,14 @@ struct repr_tag{};
1213

1314
template <typename T>
1415
constexpr std::string repr(T&& value, repr_tag={}){
15-
return std::to_string(value);
16+
auto visitor = rsl::serializer::_repr_impl::Serializer{};
17+
serializer::Meta<std::remove_cvref_t<T>>{}.visit(visitor, std::forward<T>(value));
18+
return visitor.finalize();
1619
}
1720

1821
template <auto V>
1922
constexpr std::string repr(Constant<V>, repr_tag={}){
20-
return std::to_string(V);
23+
return rsl::repr(V);
2124
}
2225

2326
template <typename T>

include/rsl/serializer/repr/serializer.hpp

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,70 @@
88

99
namespace rsl::serializer::_repr_impl {
1010

11-
struct SerializerBase {
12-
template <typename T, typename V>
13-
void descend(this T&& self, V&& value){
14-
Meta<std::remove_cvref_t<T>>::descend(std::forward<T>(self), std::forward<V>(value));
11+
class Serializer {
12+
std::ostringstream out;
13+
bool separate = false;
14+
std::size_t level = 0;
15+
16+
void print_indent() {
17+
// out << "\n";
18+
// out << std::string(2 * level, ' ');
19+
}
20+
21+
void print_separator() {
22+
if (separate) {
23+
out << ", ";
24+
} else {
25+
separate = true;
26+
}
27+
print_indent();
28+
}
29+
30+
void increase_nesting() {
31+
separate = false;
32+
out << '{';
33+
++level;
34+
}
35+
36+
void decrease_nesting() {
37+
--level;
38+
print_indent();
39+
out << '}';
40+
separate = true;
1541
}
16-
};
1742

18-
class Serializer : SerializerBase{
19-
std::size_t nesting_level = 0;
20-
std::ostringstream out;
2143
public:
2244
Serializer() = default;
2345

46+
std::string finalize() const {
47+
return out.str();
48+
}
49+
2450
template <has_members R, typename T>
25-
void operator()(R, T&& value) {
26-
out << '{';
27-
++nesting_level;
28-
descend(std::forward<T>(value));
29-
--nesting_level;
30-
out << '}';
51+
void operator()(R meta, T&& value) {
52+
print_separator();
53+
54+
out << identifier_of(R::info);
55+
increase_nesting();
56+
meta.descend(*this, std::forward<T>(value));
57+
decrease_nesting();
58+
}
59+
60+
template <is_iterable R, typename T>
61+
void operator()(R meta, T&& value) {
62+
print_separator();
63+
out << define_static_string(display_string_of(type_of(R::info)));
64+
increase_nesting();
65+
meta.descend(*this, std::forward<T>(value));
66+
decrease_nesting();
3167
}
3268

3369
template <typename R, typename T>
34-
requires (std::is_scalar_v<T>)
70+
requires(std::is_arithmetic_v<std::remove_cvref_t<T>>)
3571
void operator()(R, T&& value) {
72+
print_separator();
3673
out << value;
3774
}
3875
};
3976

40-
}
77+
} // namespace rsl::serializer::_repr_impl

include/rsl/serializer/xml/annotations.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ namespace rsl::xml {
44
namespace annotations {
55
struct Attribute {};
66
struct Raw {};
7+
struct Node {};
78
} // namespace annotations
89
constexpr inline annotations::Attribute attribute{};
910
constexpr inline annotations::Raw raw{};
11+
constexpr inline annotations::Node node{};
1012
} // namespace rsl::xml

include/rsl/serializer/xml/serializer.hpp

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class Serializer {
1515
XmlNode document;
1616
XmlNode* cursor = nullptr;
1717

18+
1819
static std::string stringify_value(bool value) { return value ? "true" : "false"; }
1920

2021
template <typename T>
@@ -23,9 +24,21 @@ class Serializer {
2324
return std::to_string(value);
2425
}
2526

26-
template <std::convertible_to<std::string> T>
27-
static std::string stringify_value(T const& value) {
28-
return std::string(value);
27+
static std::string stringify_value(std::string_view value) {
28+
std::string output;
29+
output.reserve(value.size());
30+
31+
for (char ch : value) {
32+
switch (ch) {
33+
case '&': output.append("&amp;"); break;
34+
case '<': output.append("&lt;"); break;
35+
case '>': output.append("&gt;"); break;
36+
case '"': output.append("&quot;"); break;
37+
case '\'': output.append("&#39;"); break;
38+
default: output.push_back(ch); break;
39+
}
40+
}
41+
return output;
2942
}
3043

3144
[[nodiscard]] std::string get_header() const {
@@ -88,8 +101,20 @@ class Serializer {
88101
}
89102
}
90103
} else if constexpr (meta::has_annotation(meta.info, ^^xml::annotations::Raw)){
104+
if constexpr (is_optional<R>) {
105+
if (!value.has_value()) {
106+
return;
107+
}
108+
}
109+
91110
cursor = cursor->add({std::string(identifier_of(meta.info))});
92-
cursor->raw_content = stringify_value(value);
111+
if constexpr (is_optional<R>) {
112+
if (value.has_value()) {
113+
cursor->raw_content = stringify_value(*value);
114+
}
115+
} else {
116+
cursor->raw_content = stringify_value(value);
117+
}
93118
cursor = cursor->parent;
94119
} else {
95120
throw std::runtime_error(std::format("Non-structural fields can only be attributes. {}",

test/serializer/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
target_sources(rsl_serialize_test PRIVATE
2+
basic.cpp
3+
)

test/serializer/basic.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include <string_view>
2+
#include <string>
3+
4+
#include <gtest/gtest.h>
5+
#include <rsl/repr>
6+
7+
TEST(Repr, EmptyStruct) {
8+
struct Test{};
9+
auto result = rsl::repr(Test{});
10+
ASSERT_EQ(result, "Test{}");
11+
}

test/xml/basic.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,34 @@ TEST(Xml, Nested) {
2222
};
2323

2424
auto result = rsl::to_xml(Outer{.foo={}});
25-
ASSERT_EQ(result, xml_head + "<Outer>\n <foo/>\n</Outer>"s);
25+
ASSERT_EQ(result, xml_head + "<Outer>\n <Inner/>\n</Outer>"s);
26+
}
27+
28+
TEST(Xml, Attributes) {
29+
struct Test{
30+
[[=rsl::xml::attribute]] std::string name;
31+
[[=rsl::xml::attribute]] unsigned test = 123;
32+
};
33+
auto obj = Test("foo");
34+
auto result = rsl::to_xml(obj);
35+
ASSERT_EQ(result, xml_head + R"(<Test test="123" name="foo"/>)"s);
36+
}
37+
38+
TEST(Xml, OptionalAttributes) {
39+
struct Test{
40+
[[=rsl::xml::attribute]] std::string name;
41+
[[=rsl::xml::attribute]] std::optional<unsigned> test;
42+
};
43+
auto obj = Test("foo");
44+
auto result = rsl::to_xml(obj);
45+
ASSERT_EQ(result, xml_head + R"(<Test name="foo"/>)"s);
46+
}
47+
48+
TEST(Xml, RawNodes) {
49+
struct Test{
50+
[[=rsl::xml::raw]] std::string foo;
51+
};
52+
auto obj = Test("zoinks");
53+
auto result = rsl::to_xml(obj);
54+
ASSERT_EQ(result, xml_head + "<Test>\n <foo>zoinks</foo>\n</Test>"s);
2655
}

0 commit comments

Comments
 (0)