GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 167, 0 excluded
0.0%
Functions:
0 of 23, 0 excluded
0.0%
Branches:
0 of 178, 0 excluded
0.0%

libs/kdl/src/eu/kdl/kdl.cc
Line Branch Exec Source
1 #include "eu/kdl/kdl.h"
2
3 #include <kdl/kdl.h>
4
5 namespace eu::kdl {
6
7 // internal helper functions
8 namespace {
9 std::string_view to_u8string_view(kdl_str const& s)
10 {
11 return std::string_view{reinterpret_cast<char const*>(s.data), s.len};
12 }
13
14 kdl_str to_kdl_str(std::string_view s)
15 {
16 return kdl_str{reinterpret_cast<char const*>(s.data()), s.size()};
17 }
18
19 std::variant<long long, double, std::string> kdl_number_to_variant(kdl_number const& n)
20 {
21 switch (n.type) {
22 case KDL_NUMBER_TYPE_INTEGER:
23 return n.integer;
24 case KDL_NUMBER_TYPE_FLOATING_POINT:
25 return n.floating_point;
26 case KDL_NUMBER_TYPE_STRING_ENCODED:
27 return std::string{to_u8string_view(n.string)};
28 default:
29 throw std::logic_error("invalid kdl_number");
30 }
31 }
32
33 std::variant<std::monostate, bool, Number, std::string> kdl_value_to_variant(kdl_value const& val)
34 {
35 switch (val.type) {
36 case KDL_TYPE_NULL:
37 return std::monostate{};
38 case KDL_TYPE_BOOLEAN:
39 return val.boolean;
40 case KDL_TYPE_NUMBER:
41 return Number{val.number};
42 case KDL_TYPE_STRING:
43 return std::string{to_u8string_view(val.string)};
44 default:
45 throw std::logic_error("invalid kdl_value");
46 }
47 }
48
49 void emit_nodes(kdl_emitter* emitter, std::vector<Node> const& nodes)
50 {
51 for (const auto& node : nodes) {
52 if (node.type_annotation().has_value()) {
53 if (!kdl_emit_node_with_type(
54 emitter, to_kdl_str(*node.type_annotation()), to_kdl_str(node.name())))
55 throw EmitterError{};
56 } else {
57 if (!kdl_emit_node(emitter, to_kdl_str(node.name()))) throw EmitterError{};
58 }
59
60 for (const auto& arg : node.args()) {
61 auto v = (kdl_value)arg;
62 if (!kdl_emit_arg(emitter, &v)) throw EmitterError{};
63 }
64
65 for (const auto& [key, value] : node.properties()) {
66 auto v = (kdl_value)value;
67 if (!kdl_emit_property(emitter, to_kdl_str(key), &v)) throw EmitterError{};
68 }
69
70 if (!node.children().empty()) {
71 if (!kdl_start_emitting_children(emitter)) throw EmitterError{};
72 emit_nodes(emitter, node.children());
73 if (!kdl_finish_emitting_children(emitter)) throw EmitterError{};
74 }
75 }
76 }
77
78 } // namespace
79
80 ParseError::ParseError(kdl_str const& msg) : m_msg{msg.data, msg.len} {}
81
82 Number::Number(kdl_number const& n) : m_value{kdl_number_to_variant(n)} {}
83
84 Number::operator kdl_number() const
85 {
86 kdl_number result;
87 std::visit(
88 [&result](const auto& n) {
89 using T = std::decay_t<decltype(n)>;
90 if constexpr (std::is_same_v<T, long long>) {
91 result.type = KDL_NUMBER_TYPE_INTEGER;
92 result.integer = n;
93 } else if constexpr (std::is_same_v<T, double>) {
94 result.type = KDL_NUMBER_TYPE_FLOATING_POINT;
95 result.floating_point = n;
96 } else if constexpr (std::is_same_v<T, std::string>) {
97 result.type = KDL_NUMBER_TYPE_STRING_ENCODED;
98 result.string = to_kdl_str(n);
99 } else {
100 throw std::logic_error("incomplete visit");
101 }
102 },
103 m_value);
104 return result;
105 }
106
107 Value::Value(kdl_value const& val) : m_value(kdl_value_to_variant(val))
108 {
109 if (val.type_annotation.data != nullptr) {
110 set_type_annotation(to_u8string_view(val.type_annotation));
111 }
112 }
113
114 Value Value::from_string(std::string_view s)
115 {
116 std::string doc_text = "- ";
117 doc_text.append(s);
118 auto doc = parse(doc_text);
119 if (doc.nodes().size() == 1) {
120 auto const& node = doc.nodes()[0];
121 if (node.args().size() == 1 && node.properties().empty() && node.children().empty()) {
122 return node.args()[0];
123 }
124 }
125 throw ParseError("Not a single value");
126 }
127
128 Value::operator kdl_value() const
129 {
130 kdl_value result;
131 std::visit(
132 [&result](const auto& v) {
133 using T = std::decay_t<decltype(v)>;
134 if constexpr (std::is_same_v<T, bool>) {
135 result.type = KDL_TYPE_BOOLEAN;
136 result.boolean = v;
137 } else if constexpr (std::is_same_v<T, Number>) {
138 result.type = KDL_TYPE_NUMBER;
139 result.number = (kdl_number)v;
140 } else if constexpr (std::is_same_v<T, std::string>) {
141 result.type = KDL_TYPE_STRING;
142 result.string = to_kdl_str(v);
143 } else {
144 result.type = KDL_TYPE_NULL;
145 }
146 },
147 m_value);
148 if (type_annotation().has_value()) {
149 result.type_annotation = to_kdl_str(*type_annotation());
150 } else {
151 result.type_annotation = {nullptr, 0};
152 }
153 return result;
154 }
155
156 Document Document::read_from(kdl_parser* parser)
157 {
158 Document doc;
159 auto* node_list = &doc.nodes();
160 Node* current_node = nullptr;
161 std::vector<Node*> stack;
162
163 while (true) {
164 auto* ev = kdl_parser_next_event(parser);
165
166 switch (ev->event) {
167 case KDL_EVENT_EOF:
168 return doc;
169 case KDL_EVENT_PARSE_ERROR:
170 throw ParseError(ev->value.string);
171 case KDL_EVENT_START_NODE: {
172 auto name = to_u8string_view(ev->name);
173 if (ev->value.type_annotation.data != nullptr) {
174 auto ta = to_u8string_view(ev->value.type_annotation);
175 current_node = &node_list->emplace_back(ta, name);
176 } else {
177 current_node = &node_list->emplace_back(name);
178 }
179 node_list = &current_node->children();
180 stack.push_back(current_node);
181 break;
182 }
183 case KDL_EVENT_END_NODE:
184 stack.pop_back();
185 if (stack.empty()) {
186 current_node = nullptr;
187 node_list = &doc.nodes();
188 } else {
189 current_node = stack.back();
190 node_list = &current_node->children();
191 }
192 break;
193 case KDL_EVENT_ARGUMENT:
194 current_node->args().emplace_back(Value{ev->value});
195 break;
196 case KDL_EVENT_PROPERTY:
197 current_node->properties()[std::string{to_u8string_view(ev->name)}] = Value{ev->value};
198 break;
199 default:
200 throw std::logic_error("Invalid event from kdl_parser");
201 }
202 }
203 }
204
205 std::string Document::to_string() const { return to_string(KdlVersion::Kdl_1); }
206
207 std::string Document::to_string(KdlVersion version) const
208 {
209 kdl_emitter_options opts = KDL_DEFAULT_EMITTER_OPTIONS;
210
211 if (version == KdlVersion::Kdl_1) opts.version = KDL_VERSION_1;
212 if (version == KdlVersion::Kdl_2) opts.version = KDL_VERSION_2;
213
214 kdl_emitter* emitter = kdl_create_buffering_emitter(&opts);
215 if (emitter == nullptr) throw EmitterError{"Error initializing the KDL emitter"};
216 emit_nodes(emitter, m_nodes);
217 auto result = std::string{to_u8string_view(kdl_get_emitter_buffer(emitter))};
218 kdl_destroy_emitter(emitter);
219 return result;
220 }
221
222 Document parse(std::string_view kdl_text) { return parse(kdl_text, KdlVersion::Any); }
223
224 Document parse(std::string_view kdl_text, KdlVersion version)
225 {
226 kdl_parse_option opts = KDL_DEFAULTS;
227 if (version == KdlVersion::Kdl_1) opts = KDL_READ_VERSION_1;
228 else if (version == KdlVersion::Kdl_2) opts = KDL_READ_VERSION_2;
229 else if (version == KdlVersion::Any) {
230 try {
231 return parse(kdl_text, KdlVersion::Kdl_2);
232 } catch (ParseError const&) {
233 return parse(kdl_text, KdlVersion::Kdl_1);
234 }
235 }
236
237 kdl_str text = {reinterpret_cast<char const*>(kdl_text.data()), kdl_text.size()};
238 kdl_parser* parser = kdl_create_string_parser(text, opts);
239 if (parser == nullptr) throw std::runtime_error("Error initializing the KDL parser");
240 auto doc = Document::read_from(parser);
241 kdl_destroy_parser(parser);
242 return doc;
243 }
244
245 } // namespace kdl
246