2 * Copyright (c) 2015, Vsevolod Stakhov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 // C++11 API inspired by json11: https://github.com/dropbox/json11/
39 struct ucl_map_construct_t { };
40 constexpr ucl_map_construct_t ucl_map_construct = ucl_map_construct_t();
41 struct ucl_array_construct_t { };
42 constexpr ucl_array_construct_t ucl_array_construct = ucl_array_construct_t();
48 void operator() (ucl_object_t *obj) {
49 ucl_object_unref (obj);
54 append_char (unsigned char c, size_t nchars, void *ud)
56 std::string *out = reinterpret_cast<std::string *>(ud);
58 out->append (nchars, (char)c);
63 append_len (unsigned const char *str, size_t len, void *ud)
65 std::string *out = reinterpret_cast<std::string *>(ud);
67 out->append ((const char *)str, len);
72 append_int (int64_t elt, void *ud)
74 std::string *out = reinterpret_cast<std::string *>(ud);
75 auto nstr = std::to_string (elt);
82 append_double (double elt, void *ud)
84 std::string *out = reinterpret_cast<std::string *>(ud);
85 auto nstr = std::to_string (elt);
92 static struct ucl_emitter_functions default_emit_funcs()
94 struct ucl_emitter_functions func = {
106 static bool ucl_variable_getter(const unsigned char *data, size_t len,
107 unsigned char ** /*replace*/, size_t * /*replace_len*/, bool *need_free, void* ud)
111 auto vars = reinterpret_cast<std::set<std::string> *>(ud);
112 if (vars && data && len != 0) {
113 vars->emplace (data, data + len);
118 static bool ucl_variable_replacer (const unsigned char *data, size_t len,
119 unsigned char **replace, size_t *replace_len, bool *need_free, void* ud)
123 auto replacer = reinterpret_cast<variable_replacer *>(ud);
128 std::string var_name (data, data + len);
129 if (!replacer->is_variable (var_name)) {
133 std::string var_value = replacer->replace (var_name);
134 if (var_value.empty ()) {
138 *replace = (unsigned char *)UCL_ALLOC (var_value.size ());
139 memcpy (*replace, var_value.data (), var_value.size ());
141 *replace_len = var_value.size ();
147 template <typename C, typename P>
148 static Ucl parse_with_strategy_function (C config_func, P parse_func, std::string &err)
150 auto parser = ucl_parser_new (UCL_PARSER_DEFAULT);
152 config_func (parser);
154 if (!parse_func (parser)) {
155 err.assign (ucl_parser_get_error (parser));
156 ucl_parser_free (parser);
161 auto obj = ucl_parser_get_object (parser);
162 ucl_parser_free (parser);
164 // Obj will handle ownership
168 std::unique_ptr<ucl_object_t, ucl_deleter> obj;
171 class const_iterator {
173 struct ucl_iter_deleter {
174 void operator() (ucl_object_iter_t it) {
175 ucl_object_iterate_free (it);
178 std::shared_ptr<void> it;
179 std::unique_ptr<Ucl> cur;
181 typedef std::forward_iterator_tag iterator_category;
183 const_iterator(const Ucl &obj) {
184 it = std::shared_ptr<void>(ucl_object_iterate_new (obj.obj.get()),
186 cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true)));
187 if (cur->type() == UCL_NULL) {
194 const_iterator(const const_iterator &other) = delete;
195 const_iterator(const_iterator &&other) = default;
198 const_iterator& operator=(const const_iterator &other) = delete;
199 const_iterator& operator=(const_iterator &&other) = default;
201 bool operator==(const const_iterator &other) const
203 if (cur && other.cur) {
204 return cur->obj.get() == other.cur->obj.get();
207 return !cur && !other.cur;
210 bool operator!=(const const_iterator &other) const
212 return !(*this == other);
215 const_iterator& operator++()
218 cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true)));
221 if (cur && cur->type() == UCL_NULL) {
229 const Ucl& operator*() const
233 const Ucl* operator->() const
239 struct variable_replacer {
240 virtual ~variable_replacer() {}
242 virtual bool is_variable (const std::string &str) const
244 return !str.empty ();
247 virtual std::string replace (const std::string &var) const = 0;
250 // We grab ownership if get non-const ucl_object_t
251 Ucl(ucl_object_t *other) {
256 Ucl(const ucl_object_t *other) {
257 obj.reset (ucl_object_ref (other));
260 Ucl(const Ucl &other) {
261 obj.reset (ucl_object_ref (other.obj.get()));
265 obj.swap (other.obj);
269 obj.reset (ucl_object_typed_new (UCL_NULL));
271 Ucl(std::nullptr_t) noexcept {
272 obj.reset (ucl_object_typed_new (UCL_NULL));
275 obj.reset (ucl_object_typed_new (UCL_FLOAT));
276 obj->value.dv = value;
279 obj.reset (ucl_object_typed_new (UCL_INT));
280 obj->value.iv = value;
283 obj.reset (ucl_object_typed_new (UCL_BOOLEAN));
284 obj->value.iv = static_cast<int64_t>(value);
286 Ucl(const std::string &value) {
287 obj.reset (ucl_object_fromstring_common (value.data (), value.size (),
290 Ucl(const char *value) {
291 obj.reset (ucl_object_fromstring_common (value, 0, UCL_STRING_RAW));
294 // Implicit constructor: anything with a to_json() function.
295 template <class T, class = decltype(&T::to_ucl)>
296 Ucl(const T &t) : Ucl(t.to_ucl()) {}
298 // Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
299 template <class M, typename std::enable_if<
300 std::is_constructible<std::string, typename M::key_type>::value
301 && std::is_constructible<Ucl, typename M::mapped_type>::value,
304 obj.reset (ucl_object_typed_new (UCL_OBJECT));
305 auto cobj = obj.get ();
307 for (const auto &e : m) {
308 ucl_object_insert_key (cobj, ucl_object_ref (e.second.obj.get()),
309 e.first.data (), e.first.size (), true);
313 // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
314 template <class V, typename std::enable_if<
315 std::is_constructible<Ucl, typename V::value_type>::value,
318 obj.reset (ucl_object_typed_new (UCL_ARRAY));
319 auto cobj = obj.get ();
321 for (const auto &e : v) {
322 ucl_array_append (cobj, ucl_object_ref (e.obj.get()));
326 ucl_type_t type () const {
328 return ucl_object_type (obj.get ());
333 const std::string key () const {
337 res.assign (obj->key, obj->keylen);
343 double number_value (const double default_val = 0.0) const
347 if (ucl_object_todouble_safe(obj.get(), &res)) {
354 int64_t int_value (const int64_t default_val = 0) const
358 if (ucl_object_toint_safe(obj.get(), &res)) {
365 bool bool_value (const bool default_val = false) const
369 if (ucl_object_toboolean_safe(obj.get(), &res)) {
376 const std::string string_value (const std::string& default_val = "") const
378 const char* res = nullptr;
380 if (ucl_object_tostring_safe(obj.get(), &res)) {
387 const Ucl at (size_t i) const
389 if (type () == UCL_ARRAY) {
390 return Ucl (ucl_array_find_index (obj.get(), i));
393 return Ucl (nullptr);
396 const Ucl lookup (const std::string &key) const
398 if (type () == UCL_OBJECT) {
399 return Ucl (ucl_object_lookup_len (obj.get(),
400 key.data (), key.size ()));
403 return Ucl (nullptr);
406 inline const Ucl operator[] (size_t i) const
411 inline const Ucl operator[](const std::string &key) const
416 void dump (std::string &out, ucl_emitter_t type = UCL_EMIT_JSON) const
418 struct ucl_emitter_functions cbdata;
420 cbdata = Ucl::default_emit_funcs();
421 cbdata.ud = reinterpret_cast<void *>(&out);
423 ucl_object_emit_full (obj.get(), type, &cbdata, nullptr);
426 std::string dump (ucl_emitter_t type = UCL_EMIT_JSON) const
435 static Ucl parse (const std::string &in, std::string &err)
437 return parse (in, std::map<std::string, std::string>(), err);
440 static Ucl parse (const std::string &in, const std::map<std::string, std::string> &vars, std::string &err)
442 auto config_func = [&vars] (ucl_parser *parser) {
443 for (const auto & item : vars) {
444 ucl_parser_register_variable (parser, item.first.c_str (), item.second.c_str ());
448 auto parse_func = [&in] (ucl_parser *parser) {
449 return ucl_parser_add_chunk (parser, (unsigned char *)in.data (), in.size ());
452 return parse_with_strategy_function (config_func, parse_func, err);
455 static Ucl parse (const std::string &in, const variable_replacer &replacer, std::string &err)
457 auto config_func = [&replacer] (ucl_parser *parser) {
458 ucl_parser_set_variables_handler (parser, ucl_variable_replacer,
459 &const_cast<variable_replacer &>(replacer));
462 auto parse_func = [&in] (ucl_parser *parser) {
463 return ucl_parser_add_chunk (parser, (unsigned char *) in.data (), in.size ());
466 return parse_with_strategy_function (config_func, parse_func, err);
469 static Ucl parse (const char *in, std::string &err)
471 return parse (in, std::map<std::string, std::string>(), err);
474 static Ucl parse (const char *in, const std::map<std::string, std::string> &vars, std::string &err)
480 return parse (std::string (in), vars, err);
483 static Ucl parse (const char *in, const variable_replacer &replacer, std::string &err)
489 return parse (std::string(in), replacer, err);
492 static Ucl parse_from_file (const std::string &filename, std::string &err)
494 return parse_from_file (filename, std::map<std::string, std::string>(), err);
497 static Ucl parse_from_file (const std::string &filename, const std::map<std::string, std::string> &vars, std::string &err)
499 auto config_func = [&vars] (ucl_parser *parser) {
500 for (const auto & item : vars) {
501 ucl_parser_register_variable (parser, item.first.c_str (), item.second.c_str ());
505 auto parse_func = [&filename] (ucl_parser *parser) {
506 return ucl_parser_add_file (parser, filename.c_str ());
509 return parse_with_strategy_function (config_func, parse_func, err);
512 static Ucl parse_from_file (const std::string &filename, const variable_replacer &replacer, std::string &err)
514 auto config_func = [&replacer] (ucl_parser *parser) {
515 ucl_parser_set_variables_handler (parser, ucl_variable_replacer,
516 &const_cast<variable_replacer &>(replacer));
519 auto parse_func = [&filename] (ucl_parser *parser) {
520 return ucl_parser_add_file (parser, filename.c_str ());
523 return parse_with_strategy_function (config_func, parse_func, err);
526 static std::vector<std::string> find_variable (const std::string &in)
528 auto parser = ucl_parser_new (UCL_PARSER_DEFAULT);
530 std::set<std::string> vars;
531 ucl_parser_set_variables_handler (parser, ucl_variable_getter, &vars);
532 ucl_parser_add_chunk (parser, (const unsigned char *)in.data (), in.size ());
533 ucl_parser_free (parser);
535 std::vector<std::string> result;
536 std::move (vars.begin (), vars.end (), std::back_inserter (result));
540 static std::vector<std::string> find_variable (const char *in)
543 return std::vector<std::string>();
545 return find_variable (std::string (in));
548 static std::vector<std::string> find_variable_from_file (const std::string &filename)
550 auto parser = ucl_parser_new (UCL_PARSER_DEFAULT);
552 std::set<std::string> vars;
553 ucl_parser_set_variables_handler (parser, ucl_variable_getter, &vars);
554 ucl_parser_add_file (parser, filename.c_str ());
555 ucl_parser_free (parser);
557 std::vector<std::string> result;
558 std::move (vars.begin (), vars.end (), std::back_inserter (result));
559 return std::move (result);
562 Ucl& operator= (Ucl rhs)
568 bool operator== (const Ucl &rhs) const
570 return ucl_object_compare (obj.get(), rhs.obj.get ()) == 0;
572 bool operator< (const Ucl &rhs) const
574 return ucl_object_compare (obj.get(), rhs.obj.get ()) < 0;
576 bool operator!= (const Ucl &rhs) const { return !(*this == rhs); }
577 bool operator<= (const Ucl &rhs) const { return !(rhs < *this); }
578 bool operator> (const Ucl &rhs) const { return (rhs < *this); }
579 bool operator>= (const Ucl &rhs) const { return !(*this < rhs); }
581 explicit operator bool () const
583 if (!obj || type() == UCL_NULL) {
587 if (type () == UCL_BOOLEAN) {
588 return bool_value ();
594 const_iterator begin() const
596 return const_iterator(*this);
598 const_iterator cbegin() const
600 return const_iterator(*this);
602 const_iterator end() const
604 return const_iterator();
606 const_iterator cend() const
608 return const_iterator();