xref: /freebsd/contrib/libucl/include/ucl++.h (revision d6b92ffa)
1 /*
2  * Copyright (c) 2015, Vsevolod Stakhov
3  * All rights reserved.
4  *
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.
12  *
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.
23  */
24 
25 #pragma once
26 #include <string>
27 #include <vector>
28 #include <map>
29 #include <set>
30 #include <memory>
31 #include <iostream>
32 
33 #include "ucl.h"
34 
35 // C++11 API inspired by json11: https://github.com/dropbox/json11/
36 
37 namespace ucl {
38 
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();
43 
44 class Ucl final {
45 private:
46 
47 	struct ucl_deleter {
48 		void operator() (ucl_object_t *obj) {
49 			ucl_object_unref (obj);
50 		}
51 	};
52 
53 	static int
54 	append_char (unsigned char c, size_t nchars, void *ud)
55 	{
56 		std::string *out = reinterpret_cast<std::string *>(ud);
57 
58 		out->append (nchars, (char)c);
59 
60 		return nchars;
61 	}
62 	static int
63 	append_len (unsigned const char *str, size_t len, void *ud)
64 	{
65 		std::string *out = reinterpret_cast<std::string *>(ud);
66 
67 		out->append ((const char *)str, len);
68 
69 		return len;
70 	}
71 	static int
72 	append_int (int64_t elt, void *ud)
73 	{
74 		std::string *out = reinterpret_cast<std::string *>(ud);
75 		auto nstr = std::to_string (elt);
76 
77 		out->append (nstr);
78 
79 		return nstr.size ();
80 	}
81 	static int
82 	append_double (double elt, void *ud)
83 	{
84 		std::string *out = reinterpret_cast<std::string *>(ud);
85 		auto nstr = std::to_string (elt);
86 
87 		out->append (nstr);
88 
89 		return nstr.size ();
90 	}
91 
92 	static struct ucl_emitter_functions default_emit_funcs()
93 	{
94 		struct ucl_emitter_functions func = {
95 			Ucl::append_char,
96 			Ucl::append_len,
97 			Ucl::append_int,
98 			Ucl::append_double,
99 			nullptr,
100 			nullptr
101 		};
102 
103 		return func;
104 	};
105 
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)
108 	{
109         *need_free = false;
110 
111 		auto vars = reinterpret_cast<std::set<std::string> *>(ud);
112 		if (vars && data && len != 0) {
113 			vars->emplace (data, data + len);
114 		}
115 		return false;
116 	}
117 
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)
120 	{
121 		*need_free = false;
122 
123 		auto replacer = reinterpret_cast<variable_replacer *>(ud);
124 		if (!replacer) {
125 			return false;
126         }
127 
128 		std::string var_name (data, data + len);
129 		if (!replacer->is_variable (var_name)) {
130 			return false;
131         }
132 
133 		std::string var_value = replacer->replace (var_name);
134 		if (var_value.empty ()) {
135 			return false;
136         }
137 
138 		*replace = (unsigned char *)UCL_ALLOC (var_value.size ());
139 		memcpy (*replace, var_value.data (), var_value.size ());
140 
141 		*replace_len = var_value.size ();
142 		*need_free = true;
143 
144 		return true;
145 	}
146 
147 	template <typename C, typename P>
148 	static Ucl parse_with_strategy_function (C config_func, P parse_func, std::string &err)
149 	{
150 		auto parser = ucl_parser_new (UCL_PARSER_DEFAULT);
151 
152 		config_func (parser);
153 
154 		if (!parse_func (parser)) {
155 			err.assign (ucl_parser_get_error (parser));
156 			ucl_parser_free (parser);
157 
158 			return nullptr;
159 		}
160 
161 		auto obj = ucl_parser_get_object (parser);
162 		ucl_parser_free (parser);
163 
164 		// Obj will handle ownership
165 		return Ucl (obj);
166 	}
167 
168 	std::unique_ptr<ucl_object_t, ucl_deleter> obj;
169 
170 public:
171 	class const_iterator {
172 	private:
173 		struct ucl_iter_deleter {
174 			void operator() (ucl_object_iter_t it) {
175 				ucl_object_iterate_free (it);
176 			}
177 		};
178 		std::shared_ptr<void> it;
179 		std::unique_ptr<Ucl> cur;
180 	public:
181 		typedef std::forward_iterator_tag iterator_category;
182 
183 		const_iterator(const Ucl &obj) {
184 			it = std::shared_ptr<void>(ucl_object_iterate_new (obj.obj.get()),
185 				ucl_iter_deleter());
186 			cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true)));
187 			if (cur->type() == UCL_NULL) {
188 				it.reset ();
189 				cur.reset ();
190 			}
191 		}
192 
193 		const_iterator() {}
194 		const_iterator(const const_iterator &other) = delete;
195 		const_iterator(const_iterator &&other) = default;
196 		~const_iterator() {}
197 
198 		const_iterator& operator=(const const_iterator &other) = delete;
199 		const_iterator& operator=(const_iterator &&other) = default;
200 
201 		bool operator==(const const_iterator &other) const
202 		{
203 			if (cur && other.cur) {
204 				return cur->obj.get() == other.cur->obj.get();
205 			}
206 
207 			return !cur && !other.cur;
208 		}
209 
210 		bool operator!=(const const_iterator &other) const
211 		{
212 			return !(*this == other);
213 		}
214 
215 		const_iterator& operator++()
216 		{
217 			if (it) {
218 				cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true)));
219 			}
220 
221 			if (cur && cur->type() == UCL_NULL) {
222 				it.reset ();
223 				cur.reset ();
224 			}
225 
226 			return *this;
227 		}
228 
229 		const Ucl& operator*() const
230 		{
231 			return *cur;
232 		}
233 		const Ucl* operator->() const
234 		{
235 			return cur.get();
236 		}
237 	};
238 
239 	struct variable_replacer {
240 		virtual ~variable_replacer() {}
241 
242 		virtual bool is_variable (const std::string &str) const
243 		{
244 			return !str.empty ();
245 		}
246 
247 		virtual std::string replace (const std::string &var) const = 0;
248 	};
249 
250 	// We grab ownership if get non-const ucl_object_t
251 	Ucl(ucl_object_t *other) {
252 		obj.reset (other);
253 	}
254 
255 	// Shared ownership
256 	Ucl(const ucl_object_t *other) {
257 		obj.reset (ucl_object_ref (other));
258 	}
259 
260 	Ucl(const Ucl &other) {
261 		obj.reset (ucl_object_ref (other.obj.get()));
262 	}
263 
264 	Ucl(Ucl &&other) {
265 		obj.swap (other.obj);
266 	}
267 
268 	Ucl() noexcept {
269 		obj.reset (ucl_object_typed_new (UCL_NULL));
270 	}
271 	Ucl(std::nullptr_t) noexcept {
272 		obj.reset (ucl_object_typed_new (UCL_NULL));
273 	}
274 	Ucl(double value) {
275 		obj.reset (ucl_object_typed_new (UCL_FLOAT));
276 		obj->value.dv = value;
277 	}
278 	Ucl(int64_t value) {
279 		obj.reset (ucl_object_typed_new (UCL_INT));
280 		obj->value.iv = value;
281 	}
282 	Ucl(bool value) {
283 		obj.reset (ucl_object_typed_new (UCL_BOOLEAN));
284 		obj->value.iv = static_cast<int64_t>(value);
285 	}
286 	Ucl(const std::string &value) {
287 		obj.reset (ucl_object_fromstring_common (value.data (), value.size (),
288 				UCL_STRING_RAW));
289 	}
290 	Ucl(const char *value) {
291 		obj.reset (ucl_object_fromstring_common (value, 0, UCL_STRING_RAW));
292 	}
293 
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()) {}
297 
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,
302 		int>::type = 0>
303 	Ucl(const M &m) {
304 		obj.reset (ucl_object_typed_new (UCL_OBJECT));
305 		auto cobj = obj.get ();
306 
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);
310 		}
311 	}
312 
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,
316 		int>::type = 0>
317 	Ucl(const V &v) {
318 		obj.reset (ucl_object_typed_new (UCL_ARRAY));
319 		auto cobj = obj.get ();
320 
321 		for (const auto &e : v) {
322 			ucl_array_append (cobj, ucl_object_ref (e.obj.get()));
323 		}
324 	}
325 
326 	ucl_type_t type () const {
327 		if (obj) {
328 			return ucl_object_type (obj.get ());
329 		}
330 		return UCL_NULL;
331 	}
332 
333 	const std::string key () const {
334 		std::string res;
335 
336 		if (obj->key) {
337 			res.assign (obj->key, obj->keylen);
338 		}
339 
340 		return res;
341 	}
342 
343 	double number_value (const double default_val = 0.0) const
344 	{
345 		double res;
346 
347 		if (ucl_object_todouble_safe(obj.get(), &res)) {
348 			return res;
349 		}
350 
351 		return default_val;
352 	}
353 
354 	int64_t int_value (const int64_t default_val = 0) const
355 	{
356 		int64_t res;
357 
358 		if (ucl_object_toint_safe(obj.get(), &res)) {
359 			return res;
360 		}
361 
362 		return default_val;
363 	}
364 
365 	bool bool_value (const bool default_val = false) const
366 	{
367 		bool res;
368 
369 		if (ucl_object_toboolean_safe(obj.get(), &res)) {
370 			return res;
371 		}
372 
373 		return default_val;
374 	}
375 
376 	const std::string string_value (const std::string& default_val = "") const
377 	{
378 		const char* res = nullptr;
379 
380 		if (ucl_object_tostring_safe(obj.get(), &res)) {
381 			return res;
382 		}
383 
384 		return default_val;
385 	}
386 
387 	const Ucl at (size_t i) const
388 	{
389 		if (type () == UCL_ARRAY) {
390 			return Ucl (ucl_array_find_index (obj.get(), i));
391 		}
392 
393 		return Ucl (nullptr);
394 	}
395 
396 	const Ucl lookup (const std::string &key) const
397 	{
398 		if (type () == UCL_OBJECT) {
399 			return Ucl (ucl_object_lookup_len (obj.get(),
400 					key.data (), key.size ()));
401 		}
402 
403 		return Ucl (nullptr);
404 	}
405 
406 	inline const Ucl operator[] (size_t i) const
407 	{
408 		return at(i);
409 	}
410 
411 	inline const Ucl operator[](const std::string &key) const
412 	{
413 		return lookup(key);
414 	}
415 	// Serialize.
416 	void dump (std::string &out, ucl_emitter_t type = UCL_EMIT_JSON) const
417 	{
418 		struct ucl_emitter_functions cbdata;
419 
420 		cbdata = Ucl::default_emit_funcs();
421 		cbdata.ud = reinterpret_cast<void *>(&out);
422 
423 		ucl_object_emit_full (obj.get(), type, &cbdata, nullptr);
424 	}
425 
426 	std::string dump (ucl_emitter_t type = UCL_EMIT_JSON) const
427 	{
428 		std::string out;
429 
430 		dump (out, type);
431 
432 		return out;
433 	}
434 
435 	static Ucl parse (const std::string &in, std::string &err)
436 	{
437 		return parse (in, std::map<std::string, std::string>(), err);
438 	}
439 
440 	static Ucl parse (const std::string &in, const std::map<std::string, std::string> &vars, std::string &err)
441 	{
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 ());
445             }
446 		};
447 
448 		auto parse_func = [&in] (ucl_parser *parser) {
449 			return ucl_parser_add_chunk (parser, (unsigned char *)in.data (), in.size ());
450 		};
451 
452 		return parse_with_strategy_function (config_func, parse_func, err);
453 	}
454 
455 	static Ucl parse (const std::string &in, const variable_replacer &replacer, std::string &err)
456 	{
457 		auto config_func = [&replacer] (ucl_parser *parser) {
458 			ucl_parser_set_variables_handler (parser, ucl_variable_replacer,
459 				&const_cast<variable_replacer &>(replacer));
460 		};
461 
462 		auto parse_func = [&in] (ucl_parser *parser) {
463 			return ucl_parser_add_chunk (parser, (unsigned char *) in.data (), in.size ());
464 		};
465 
466 		return parse_with_strategy_function (config_func, parse_func, err);
467 	}
468 
469 	static Ucl parse (const char *in, std::string &err)
470 	{
471 		return parse (in, std::map<std::string, std::string>(), err);
472 	}
473 
474 	static Ucl parse (const char *in, const std::map<std::string, std::string> &vars, std::string &err)
475 	{
476 		if (!in) {
477 			err = "null input";
478 			return nullptr;
479 		}
480 		return parse (std::string (in), vars, err);
481 	}
482 
483 	static Ucl parse (const char *in, const variable_replacer &replacer, std::string &err)
484 	{
485 		if (!in) {
486 			err = "null input";
487 			return nullptr;
488 		}
489 		return parse (std::string(in), replacer, err);
490 	}
491 
492 	static Ucl parse_from_file (const std::string &filename, std::string &err)
493 	{
494 		return parse_from_file (filename, std::map<std::string, std::string>(), err);
495 	}
496 
497 	static Ucl parse_from_file (const std::string &filename, const std::map<std::string, std::string> &vars, std::string &err)
498 	{
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 ());
502             }
503 		};
504 
505 		auto parse_func = [&filename] (ucl_parser *parser) {
506 			return ucl_parser_add_file (parser, filename.c_str ());
507 		};
508 
509 		return parse_with_strategy_function (config_func, parse_func, err);
510 	}
511 
512 	static Ucl parse_from_file (const std::string &filename, const variable_replacer &replacer, std::string &err)
513 	{
514 		auto config_func = [&replacer] (ucl_parser *parser) {
515 			ucl_parser_set_variables_handler (parser, ucl_variable_replacer,
516 				&const_cast<variable_replacer &>(replacer));
517 		};
518 
519 		auto parse_func = [&filename] (ucl_parser *parser) {
520 			return ucl_parser_add_file (parser, filename.c_str ());
521 		};
522 
523 		return parse_with_strategy_function (config_func, parse_func, err);
524 	}
525 
526 	static std::vector<std::string> find_variable (const std::string &in)
527 	{
528 		auto parser = ucl_parser_new (UCL_PARSER_DEFAULT);
529 
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);
534 
535 		std::vector<std::string> result;
536 		std::move (vars.begin (), vars.end (), std::back_inserter (result));
537 		return result;
538 	}
539 
540 	static std::vector<std::string> find_variable (const char *in)
541 	{
542 		if (!in) {
543 			return std::vector<std::string>();
544 		}
545 		return find_variable (std::string (in));
546 	}
547 
548 	static std::vector<std::string> find_variable_from_file (const std::string &filename)
549 	{
550 		auto parser = ucl_parser_new (UCL_PARSER_DEFAULT);
551 
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);
556 
557 		std::vector<std::string> result;
558 		std::move (vars.begin (), vars.end (), std::back_inserter (result));
559 		return std::move (result);
560 	}
561 
562 	Ucl& operator= (Ucl rhs)
563 	{
564 		obj.swap (rhs.obj);
565 		return *this;
566 	}
567 
568 	bool operator== (const Ucl &rhs) const
569 	{
570 		return ucl_object_compare (obj.get(), rhs.obj.get ()) == 0;
571 	}
572 	bool operator< (const Ucl &rhs) const
573 	{
574 		return ucl_object_compare (obj.get(), rhs.obj.get ()) < 0;
575 	}
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); }
580 
581 	explicit operator bool () const
582 	{
583 		if (!obj || type() == UCL_NULL) {
584 			return false;
585 		}
586 
587 		if (type () == UCL_BOOLEAN) {
588 			return bool_value ();
589 		}
590 
591 		return true;
592 	}
593 
594 	const_iterator begin() const
595 	{
596 		return const_iterator(*this);
597 	}
598 	const_iterator cbegin() const
599 	{
600 		return const_iterator(*this);
601 	}
602 	const_iterator end() const
603 	{
604 		return const_iterator();
605 	}
606 	const_iterator cend() const
607 	{
608 		return const_iterator();
609 	}
610 };
611 
612 };
613