1 // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
2 // the main distribution directory for license terms and copyright or visit
3 // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
4
5 #include "caf/config_value_writer.hpp"
6
7 #include "caf/config_value.hpp"
8 #include "caf/detail/append_hex.hpp"
9 #include "caf/detail/overload.hpp"
10 #include "caf/settings.hpp"
11
12 #define CHECK_NOT_EMPTY() \
13 do { \
14 if (st_.empty()) { \
15 emplace_error(sec::runtime_error, "mismatching calls to begin/end"); \
16 return false; \
17 } \
18 } while (false)
19
20 #define CHECK_VALID() \
21 do { \
22 CHECK_NOT_EMPTY(); \
23 if (holds_alternative<none_t>(st_.top())) { \
24 emplace_error(sec::runtime_error, \
25 "attempted to write to a non-existent optional field"); \
26 return false; \
27 } \
28 } while (false)
29
30 #define SCOPE(top_type) \
31 CHECK_VALID(); \
32 if (!holds_alternative<top_type>(st_.top())) { \
33 if constexpr (std::is_same<top_type, settings>::value) { \
34 emplace_error(sec::runtime_error, \
35 "attempted to add list items before calling " \
36 "begin_sequence or begin_tuple"); \
37 } else { \
38 emplace_error(sec::runtime_error, \
39 "attempted to add fields to a list item"); \
40 } \
41 return false; \
42 } \
43 [[maybe_unused]] auto& top = get<top_type>(st_.top());
44
45 namespace caf {
46
47 // -- constructors, destructors, and assignment operators ----------------------
48
~config_value_writer()49 config_value_writer::~config_value_writer() {
50 // nop
51 }
52
53 // -- interface functions ------------------------------------------------------
54
begin_object(type_id_t type,string_view)55 bool config_value_writer::begin_object(type_id_t type, string_view) {
56 CHECK_NOT_EMPTY();
57 auto f = detail::make_overload(
58 [this](config_value* x) {
59 // Morph the root element into a dictionary.
60 auto& dict = x->as_dictionary();
61 dict.clear();
62 st_.top() = &dict;
63 return true;
64 },
65 [this](settings*) {
66 emplace_error(sec::runtime_error,
67 "begin_object called inside another object");
68 return false;
69 },
70 [this](absent_field) {
71 emplace_error(sec::runtime_error,
72 "begin_object called inside non-existent optional field");
73 return false;
74 },
75 [this](present_field fld) {
76 CAF_ASSERT(fld.parent != nullptr);
77 auto [iter, added] = fld.parent->emplace(fld.name, settings{});
78 if (!added) {
79 emplace_error(sec::runtime_error,
80 "field already defined: " + to_string(fld.name));
81 return false;
82 }
83 auto obj = std::addressof(get<settings>(iter->second));
84 if (!fld.type.empty())
85 put(*obj, "@type", fld.type);
86 st_.push(obj);
87 return true;
88 },
89 [this](config_value::list* ls) {
90 ls->emplace_back(settings{});
91 st_.push(std::addressof(get<settings>(ls->back())));
92 return true;
93 });
94 if (!visit(f, st_.top()))
95 return false;
96 if (type != invalid_type_id)
97 put(*get<settings*>(st_.top()), "@type", query_type_name(type));
98 return true;
99 }
100
end_object()101 bool config_value_writer::end_object() {
102 SCOPE(settings*);
103 st_.pop();
104 return true;
105 }
106
begin_field(string_view name)107 bool config_value_writer::begin_field(string_view name) {
108 SCOPE(settings*);
109 st_.push(present_field{top, name, string_view{}});
110 return true;
111 }
112
begin_field(string_view name,bool is_present)113 bool config_value_writer::begin_field(string_view name, bool is_present) {
114 SCOPE(settings*);
115 if (is_present)
116 st_.push(present_field{top, name, string_view{}});
117 else
118 st_.push(absent_field{});
119 return true;
120 }
121
begin_field(string_view name,span<const type_id_t> types,size_t index)122 bool config_value_writer::begin_field(string_view name,
123 span<const type_id_t> types,
124 size_t index) {
125 SCOPE(settings*);
126 if (index >= types.size()) {
127 emplace_error(sec::invalid_argument,
128 "index out of range in optional variant field "
129 + to_string(name));
130 return false;
131 }
132 auto tn = query_type_name(types[index]);
133 if (tn.empty()) {
134 emplace_error(sec::runtime_error,
135 "query_type_name returned an empty string for type ID");
136 return false;
137 }
138 st_.push(present_field{top, name, tn});
139 return true;
140 }
141
begin_field(string_view name,bool is_present,span<const type_id_t> types,size_t index)142 bool config_value_writer::begin_field(string_view name, bool is_present,
143 span<const type_id_t> types,
144 size_t index) {
145 if (is_present)
146 return begin_field(name, types, index);
147 else
148 return begin_field(name, false);
149 }
150
end_field()151 bool config_value_writer::end_field() {
152 CHECK_NOT_EMPTY();
153 if (!holds_alternative<present_field>(st_.top())
154 && !holds_alternative<absent_field>(st_.top())) {
155 emplace_error(sec::runtime_error, "end_field called outside of a field");
156 return false;
157 }
158 st_.pop();
159 return true;
160 }
161
begin_tuple(size_t size)162 bool config_value_writer::begin_tuple(size_t size) {
163 return begin_sequence(size);
164 }
165
end_tuple()166 bool config_value_writer::end_tuple() {
167 return end_sequence();
168 }
169
begin_key_value_pair()170 bool config_value_writer::begin_key_value_pair() {
171 SCOPE(settings*);
172 auto [iter, added] = top->emplace("@tmp", config_value::list{});
173 if (!added) {
174 emplace_error(sec::runtime_error, "temporary entry @tmp already exists");
175 return false;
176 }
177 st_.push(std::addressof(get<config_value::list>(iter->second)));
178 return true;
179 }
180
end_key_value_pair()181 bool config_value_writer::end_key_value_pair() {
182 config_value::list tmp;
183 /* lifetime scope of the list */ {
184 SCOPE(config_value::list*);
185 if (top->size() != 2) {
186 emplace_error(sec::runtime_error,
187 "a key-value pair must have exactly two elements");
188 return false;
189 }
190 tmp = std::move(*top);
191 st_.pop();
192 }
193 SCOPE(settings*);
194 // Get key and value from the temporary list.
195 top->container().erase("@tmp");
196 std::string key;
197 if (auto str = get_if<std::string>(std::addressof(tmp[0])))
198 key = std::move(*str);
199 else
200 key = to_string(tmp[0]);
201 if (!top->emplace(std::move(key), std::move(tmp[1])).second) {
202 emplace_error(sec::runtime_error, "multiple definitions for key");
203 return false;
204 }
205 return true;
206 }
207
begin_sequence(size_t)208 bool config_value_writer::begin_sequence(size_t) {
209 CHECK_NOT_EMPTY();
210 auto f = detail::make_overload(
211 [this](config_value* val) {
212 // Morph the value into a list.
213 auto& ls = val->as_list();
214 ls.clear();
215 st_.top() = &ls;
216 return true;
217 },
218 [this](settings*) {
219 emplace_error(sec::runtime_error,
220 "cannot start sequence/tuple inside an object");
221 return false;
222 },
223 [this](absent_field) {
224 emplace_error(
225 sec::runtime_error,
226 "cannot start sequence/tuple inside non-existent optional field");
227 return false;
228 },
229 [this](present_field fld) {
230 auto [iter, added] = fld.parent->emplace(fld.name, config_value::list{});
231 if (!added) {
232 emplace_error(sec::runtime_error,
233 "field already defined: " + to_string(fld.name));
234 return false;
235 }
236 st_.push(std::addressof(get<config_value::list>(iter->second)));
237 return true;
238 },
239 [this](config_value::list* ls) {
240 ls->emplace_back(config_value::list{});
241 st_.push(std::addressof(get<config_value::list>(ls->back())));
242 return true;
243 });
244 return visit(f, st_.top());
245 }
246
end_sequence()247 bool config_value_writer::end_sequence() {
248 SCOPE(config_value::list*);
249 st_.pop();
250 return true;
251 }
252
begin_associative_array(size_t)253 bool config_value_writer::begin_associative_array(size_t) {
254 CHECK_NOT_EMPTY();
255 settings* inner = nullptr;
256 auto f = detail::make_overload(
257 [this, &inner](config_value* val) {
258 // Morph the top element into a dictionary.
259 auto& dict = val->as_dictionary();
260 dict.clear();
261 st_.top() = &dict;
262 inner = &dict;
263 return true;
264 },
265 [this](settings*) {
266 emplace_error(sec::runtime_error, "cannot write values outside fields");
267 return false;
268 },
269 [this](absent_field) {
270 emplace_error(sec::runtime_error,
271 "cannot add values to non-existent optional field");
272 return false;
273 },
274 [this, &inner](present_field fld) {
275 auto [iter, added] = fld.parent->emplace(fld.name,
276 config_value{settings{}});
277 if (!added) {
278 emplace_error(sec::runtime_error,
279 "field already defined: " + to_string(fld.name));
280 return false;
281 }
282 if (!fld.type.empty()) {
283 std::string key;
284 key += '@';
285 key.insert(key.end(), fld.name.begin(), fld.name.end());
286 key += "-type";
287 if (fld.parent->contains(key)) {
288 emplace_error(sec::runtime_error,
289 "type of variant field already defined.");
290 return false;
291 }
292 put(*fld.parent, key, fld.type);
293 }
294 inner = std::addressof(get<settings>(iter->second));
295 return true;
296 },
297 [&inner](config_value::list* ls) {
298 ls->emplace_back(config_value{settings{}});
299 inner = std::addressof(get<settings>(ls->back()));
300 return true;
301 });
302 if (visit(f, st_.top())) {
303 CAF_ASSERT(inner != nullptr);
304 st_.push(inner);
305 return true;
306 }
307 return false;
308 }
309
end_associative_array()310 bool config_value_writer::end_associative_array() {
311 SCOPE(settings*);
312 st_.pop();
313 return true;
314 }
315
value(byte x)316 bool config_value_writer::value(byte x) {
317 return push(config_value{static_cast<config_value::integer>(x)});
318 }
319
value(bool x)320 bool config_value_writer::value(bool x) {
321 return push(config_value{x});
322 }
323
value(int8_t x)324 bool config_value_writer::value(int8_t x) {
325 return push(config_value{static_cast<config_value::integer>(x)});
326 }
327
value(uint8_t x)328 bool config_value_writer::value(uint8_t x) {
329 return push(config_value{static_cast<config_value::integer>(x)});
330 }
331
value(int16_t x)332 bool config_value_writer::value(int16_t x) {
333 return push(config_value{static_cast<config_value::integer>(x)});
334 }
335
value(uint16_t x)336 bool config_value_writer::value(uint16_t x) {
337 return push(config_value{static_cast<config_value::integer>(x)});
338 }
339
value(int32_t x)340 bool config_value_writer::value(int32_t x) {
341 return push(config_value{static_cast<config_value::integer>(x)});
342 }
343
value(uint32_t x)344 bool config_value_writer::value(uint32_t x) {
345 return push(config_value{static_cast<config_value::integer>(x)});
346 }
347
value(int64_t x)348 bool config_value_writer::value(int64_t x) {
349 return push(config_value{static_cast<config_value::integer>(x)});
350 }
351
value(uint64_t x)352 bool config_value_writer::value(uint64_t x) {
353 auto max_val = std::numeric_limits<config_value::integer>::max();
354 if (x > static_cast<uint64_t>(max_val)) {
355 emplace_error(sec::runtime_error, "integer overflow");
356 return false;
357 }
358 return push(config_value{static_cast<config_value::integer>(x)});
359 }
360
value(float x)361 bool config_value_writer::value(float x) {
362 return push(config_value{double{x}});
363 }
364
value(double x)365 bool config_value_writer::value(double x) {
366 return push(config_value{x});
367 }
368
value(long double x)369 bool config_value_writer::value(long double x) {
370 return push(config_value{std::to_string(x)});
371 }
372
value(string_view x)373 bool config_value_writer::value(string_view x) {
374 return push(config_value{to_string(x)});
375 }
376
value(const std::u16string &)377 bool config_value_writer::value(const std::u16string&) {
378 emplace_error(sec::runtime_error, "u16string support not implemented yet");
379 return false;
380 }
381
value(const std::u32string &)382 bool config_value_writer::value(const std::u32string&) {
383 emplace_error(sec::runtime_error, "u32string support not implemented yet");
384 return false;
385 }
386
value(span<const byte> x)387 bool config_value_writer::value(span<const byte> x) {
388 std::string str;
389 detail::append_hex(str, x.data(), x.size());
390 return push(config_value{std::move(str)});
391 }
392
push(config_value && x)393 bool config_value_writer::push(config_value&& x) {
394 CHECK_NOT_EMPTY();
395 auto f = detail::make_overload(
396 [&x](config_value* val) {
397 *val = std::move(x);
398 return true;
399 },
400 [this](settings*) {
401 emplace_error(sec::runtime_error, "cannot write values outside fields");
402 return false;
403 },
404 [this](absent_field) {
405 emplace_error(sec::runtime_error,
406 "cannot add values to non-existent optional field");
407 return false;
408 },
409 [this, &x](present_field fld) {
410 auto [iter, added] = fld.parent->emplace(fld.name, std::move(x));
411 if (!added) {
412 emplace_error(sec::runtime_error,
413 "field already defined: " + to_string(fld.name));
414 return false;
415 }
416 if (!fld.type.empty()) {
417 std::string key;
418 key += '@';
419 key.insert(key.end(), fld.name.begin(), fld.name.end());
420 key += "-type";
421 if (fld.parent->contains(key)) {
422 emplace_error(sec::runtime_error,
423 "type of variant field already defined.");
424 return false;
425 }
426 put(*fld.parent, key, fld.type);
427 }
428 return true;
429 },
430 [&x](config_value::list* ls) {
431 ls->emplace_back(std::move(x));
432 return true;
433 });
434 return visit(f, st_.top());
435 }
436
437 } // namespace caf
438