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