xref: /freebsd/contrib/libucl/include/ucl++.h (revision a3cefe7f2b4df0f70ff92d4570ce18e517af43ec)
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 #include <tuple>
33 
34 #include "ucl.h"
35 
36 // C++11 API inspired by json11: https://github.com/dropbox/json11/
37 
38 namespace ucl {
39 
40 struct ucl_map_construct_t {};
41 constexpr ucl_map_construct_t ucl_map_construct = ucl_map_construct_t();
42 struct ucl_array_construct_t {};
43 constexpr ucl_array_construct_t ucl_array_construct = ucl_array_construct_t();
44 
45 class Ucl final {
46 private:
47 	struct ucl_deleter {
48 		void operator()(ucl_object_t *obj)
49 		{
50 			ucl_object_unref(obj);
51 		}
52 	};
53 
54 	static int
55 	append_char(unsigned char c, size_t nchars, void *ud)
56 	{
57 		std::string *out = reinterpret_cast<std::string *>(ud);
58 
59 		out->append(nchars, (char) c);
60 
61 		return nchars;
62 	}
63 	static int
64 	append_len(unsigned const char *str, size_t len, void *ud)
65 	{
66 		std::string *out = reinterpret_cast<std::string *>(ud);
67 
68 		out->append((const char *) str, len);
69 
70 		return len;
71 	}
72 	static int
73 	append_int(int64_t elt, void *ud)
74 	{
75 		std::string *out = reinterpret_cast<std::string *>(ud);
76 		auto nstr = std::to_string(elt);
77 
78 		out->append(nstr);
79 
80 		return nstr.size();
81 	}
82 	static int
83 	append_double(double elt, void *ud)
84 	{
85 		std::string *out = reinterpret_cast<std::string *>(ud);
86 		auto nstr = std::to_string(elt);
87 
88 		out->append(nstr);
89 
90 		return nstr.size();
91 	}
92 
93 	static struct ucl_emitter_functions default_emit_funcs()
94 	{
95 		struct ucl_emitter_functions func = {
96 			Ucl::append_char,
97 			Ucl::append_len,
98 			Ucl::append_int,
99 			Ucl::append_double,
100 			nullptr,
101 			nullptr};
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 			const char *error = ucl_parser_get_error(parser);//Assigning here without checking result first causes a
156 			if (error != NULL) err.assign(error);            //	crash if ucl_parser_get_error returns NULL
157 			ucl_parser_free(parser);
158 
159 			return nullptr;
160 		}
161 
162 		auto obj = ucl_parser_get_object(parser);
163 		ucl_parser_free(parser);
164 
165 		// Obj will handle ownership
166 		return Ucl(obj);
167 	}
168 
169 	std::unique_ptr<ucl_object_t, ucl_deleter> obj;
170 
171 public:
172 	struct macro_handler_s {
173 		ucl_macro_handler handler;
174 		ucl_context_macro_handler ctx_handler;
175 	};
176 
177 	struct macro_userdata_s {
178 		ucl_parser *parser;
179 		void *userdata;
180 	};
181 
182 	class const_iterator {
183 	private:
184 		struct ucl_iter_deleter {
185 			void operator()(ucl_object_iter_t it)
186 			{
187 				ucl_object_iterate_free(it);
188 			}
189 		};
190 		std::shared_ptr<void> it;
191 		std::unique_ptr<Ucl> cur;
192 
193 	public:
194 		typedef std::forward_iterator_tag iterator_category;
195 
196 		const_iterator(const Ucl &obj)
197 		{
198 			it = std::shared_ptr<void>(ucl_object_iterate_new(obj.obj.get()),
199 									   ucl_iter_deleter());
200 			cur.reset(new Ucl(ucl_object_iterate_safe(it.get(), true)));
201 			if (!cur->obj) {
202 				it.reset();
203 				cur.reset();
204 			}
205 		}
206 
207 		const_iterator()
208 		{
209 		}
210 		const_iterator(const const_iterator &other) = delete;
211 		const_iterator(const_iterator &&other) = default;
212 		~const_iterator()
213 		{
214 		}
215 
216 		const_iterator &operator=(const const_iterator &other) = delete;
217 		const_iterator &operator=(const_iterator &&other) = default;
218 
219 		bool operator==(const const_iterator &other) const
220 		{
221 			if (cur && other.cur) {
222 				return cur->obj.get() == other.cur->obj.get();
223 			}
224 
225 			return !cur && !other.cur;
226 		}
227 
228 		bool operator!=(const const_iterator &other) const
229 		{
230 			return !(*this == other);
231 		}
232 
233 		const_iterator &operator++()
234 		{
235 			if (it) {
236 				cur.reset(new Ucl(ucl_object_iterate_safe(it.get(), true)));
237 			}
238 
239 			if (cur && !cur->obj) {
240 				it.reset();
241 				cur.reset();
242 			}
243 
244 			return *this;
245 		}
246 
247 		const Ucl &operator*() const
248 		{
249 			return *cur;
250 		}
251 		const Ucl *operator->() const
252 		{
253 			return cur.get();
254 		}
255 	};
256 
257 	struct variable_replacer {
258 		virtual ~variable_replacer()
259 		{
260 		}
261 
262 		virtual bool is_variable(const std::string &str) const
263 		{
264 			return !str.empty();
265 		}
266 
267 		virtual std::string replace(const std::string &var) const = 0;
268 	};
269 
270 	// We grab ownership if get non-const ucl_object_t
271 	Ucl(ucl_object_t *other)
272 	{
273 		obj.reset(other);
274 	}
275 
276 	// Shared ownership
277 	Ucl(const ucl_object_t *other)
278 	{
279 		obj.reset(ucl_object_ref(other));
280 	}
281 
282 	Ucl(const Ucl &other)
283 	{
284 		obj.reset(ucl_object_ref(other.obj.get()));
285 	}
286 
287 	Ucl(Ucl &&other)
288 	{
289 		obj.swap(other.obj);
290 	}
291 
292 	Ucl() noexcept
293 	{
294 		obj.reset(ucl_object_typed_new(UCL_NULL));
295 	}
296 	Ucl(std::nullptr_t) noexcept
297 	{
298 		obj.reset(ucl_object_typed_new(UCL_NULL));
299 	}
300 	Ucl(double value)
301 	{
302 		obj.reset(ucl_object_typed_new(UCL_FLOAT));
303 		obj->value.dv = value;
304 	}
305 	Ucl(int64_t value)
306 	{
307 		obj.reset(ucl_object_typed_new(UCL_INT));
308 		obj->value.iv = value;
309 	}
310 	Ucl(bool value)
311 	{
312 		obj.reset(ucl_object_typed_new(UCL_BOOLEAN));
313 		obj->value.iv = static_cast<int64_t>(value);
314 	}
315 	Ucl(const std::string &value)
316 	{
317 		obj.reset(ucl_object_fromstring_common(value.data(), value.size(),
318 											   UCL_STRING_RAW));
319 	}
320 	Ucl(const char *value)
321 	{
322 		obj.reset(ucl_object_fromstring_common(value, 0, UCL_STRING_RAW));
323 	}
324 
325 	// Implicit constructor: anything with a to_json() function.
326 	template<class T, class = decltype(&T::to_ucl)>
327 	Ucl(const T &t)
328 		: Ucl(t.to_ucl())
329 	{
330 	}
331 
332 	// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
333 	template<class M, typename std::enable_if<
334 						  std::is_constructible<std::string, typename M::key_type>::value && std::is_constructible<Ucl, typename M::mapped_type>::value,
335 						  int>::type = 0>
336 	Ucl(const M &m)
337 	{
338 		obj.reset(ucl_object_typed_new(UCL_OBJECT));
339 		auto cobj = obj.get();
340 
341 		for (const auto &e: m) {
342 			ucl_object_insert_key(cobj, ucl_object_ref(e.second.obj.get()),
343 								  e.first.data(), e.first.size(), true);
344 		}
345 	}
346 
347 	// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
348 	template<class V, typename std::enable_if<
349 						  std::is_constructible<Ucl, typename V::value_type>::value,
350 						  int>::type = 0>
351 	Ucl(const V &v)
352 	{
353 		obj.reset(ucl_object_typed_new(UCL_ARRAY));
354 		auto cobj = obj.get();
355 
356 		for (const auto &e: v) {
357 			ucl_array_append(cobj, ucl_object_ref(e.obj.get()));
358 		}
359 	}
360 
361 	ucl_type_t type() const
362 	{
363 		if (obj) {
364 			return ucl_object_type(obj.get());
365 		}
366 		return UCL_NULL;
367 	}
368 
369 	std::string key() const
370 	{
371 		std::string res;
372 
373 		if (obj->key) {
374 			res.assign(obj->key, obj->keylen);
375 		}
376 
377 		return res;
378 	}
379 
380 	double number_value(const double default_val = 0.0) const
381 	{
382 		double res;
383 
384 		if (ucl_object_todouble_safe(obj.get(), &res)) {
385 			return res;
386 		}
387 
388 		return default_val;
389 	}
390 
391 	int64_t int_value(const int64_t default_val = 0) const
392 	{
393 		int64_t res;
394 
395 		if (ucl_object_toint_safe(obj.get(), &res)) {
396 			return res;
397 		}
398 
399 		return default_val;
400 	}
401 
402 	bool bool_value(const bool default_val = false) const
403 	{
404 		bool res;
405 
406 		if (ucl_object_toboolean_safe(obj.get(), &res)) {
407 			return res;
408 		}
409 
410 		return default_val;
411 	}
412 
413 	std::string string_value(const std::string &default_val = "") const
414 	{
415 		const char *res = nullptr;
416 
417 		if (ucl_object_tostring_safe(obj.get(), &res)) {
418 			return res;
419 		}
420 
421 		return default_val;
422 	}
423 
424 	std::string forced_string_value () const
425 	{
426 		return ucl_object_tostring_forced(obj.get());
427 	}
428 
429 	size_t size() const
430 	{
431 		if (type() == UCL_ARRAY) {
432 			return ucl_array_size(obj.get());
433 		}
434 
435 		return 0;
436 	}
437 
438 	Ucl at(size_t i) const
439 	{
440 		if (type() == UCL_ARRAY) {
441 			return Ucl(ucl_array_find_index(obj.get(), i));
442 		}
443 
444 		return Ucl(nullptr);
445 	}
446 
447 	Ucl lookup(const std::string &key) const
448 	{
449 		if (type() == UCL_OBJECT) {
450 			return Ucl(ucl_object_lookup_len(obj.get(),
451 											 key.data(), key.size()));
452 		}
453 
454 		return Ucl(nullptr);
455 	}
456 
457 	inline Ucl operator[](size_t i) const
458 	{
459 		return at(i);
460 	}
461 
462 	inline Ucl operator[](const std::string &key) const
463 	{
464 		return lookup(key);
465 	}
466 	// Serialize.
467 	void dump(std::string &out, ucl_emitter_t type = UCL_EMIT_JSON) const
468 	{
469 		struct ucl_emitter_functions cbdata;
470 
471 		cbdata = Ucl::default_emit_funcs();
472 		cbdata.ud = reinterpret_cast<void *>(&out);
473 
474 		ucl_object_emit_full(obj.get(), type, &cbdata, nullptr);
475 	}
476 
477 	std::string dump(ucl_emitter_t type = UCL_EMIT_JSON) const
478 	{
479 		std::string out;
480 
481 		dump(out, type);
482 
483 		return out;
484 	}
485 
486 	static Ucl parse(const std::string &in, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
487 	{
488 		return parse(in, std::map<std::string, std::string>(), err, duplicate_strategy);
489 	}
490 
491 	static Ucl parse(const std::string &in, const std::map<std::string, std::string> &vars,
492 					 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
493 	{
494 		std::vector<std::tuple<std::string, macro_handler_s, void *>> emptyVector;
495 		return parse(in, vars, emptyVector, err, duplicate_strategy);
496 	}
497 
498 	//Macro handler will receive a macro_userdata_s as void *ud
499 	static Ucl parse(const std::string &in,
500 					 std::vector<std::tuple<std::string /*name*/, macro_handler_s, void * /*userdata*/>> &macros,
501 					 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
502 	{
503 		return parse(in, std::map<std::string, std::string>(), macros, err, duplicate_strategy);
504 	}
505 
506 	//Macro handler will receive a macro_userdata_s as void *ud
507 	static Ucl parse(const std::string &in, const std::map<std::string, std::string> &vars,
508 					 std::vector<std::tuple<std::string /*name*/, macro_handler_s, void * /*userdata*/>> &macros,
509 					 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
510 	{
511 		//Preserve macro_userdata_s memory for later use in parse_with_strategy_function()
512 		std::vector<macro_userdata_s> userdata_list;
513 		userdata_list.reserve(macros.size());
514 		auto config_func = [&userdata_list, &vars, &macros](ucl_parser *parser) {
515 			for (const auto &item: vars) {
516 				ucl_parser_register_variable(parser, item.first.c_str(), item.second.c_str());
517 			}
518 			for (auto &macro: macros) {
519 				userdata_list.push_back({parser, std::get<2>(macro)});
520 				if (std::get<1>(macro).handler != NULL) {
521 					ucl_parser_register_macro(parser,
522 											  std::get<0>(macro).c_str(),
523 											  std::get<1>(macro).handler,
524 											  reinterpret_cast<void *>(&userdata_list.back()));
525 				}
526 				else if (std::get<1>(macro).ctx_handler != NULL) {
527 					ucl_parser_register_context_macro(parser,
528 													  std::get<0>(macro).c_str(),
529 													  std::get<1>(macro).ctx_handler,
530 													  reinterpret_cast<void *>(&userdata_list.back()));
531 				}
532 			}
533 		};
534 
535 		auto parse_func = [&in, &duplicate_strategy](struct ucl_parser *parser) -> bool {
536 			return ucl_parser_add_chunk_full(parser,
537 											 (unsigned char *) in.data(),
538 											 in.size(),
539 											 (unsigned int) ucl_parser_get_default_priority(parser),
540 											 duplicate_strategy,
541 											 UCL_PARSE_UCL);
542 		};
543 
544 		return parse_with_strategy_function(config_func, parse_func, err);
545 	}
546 
547 	static Ucl parse(const std::string &in, const variable_replacer &replacer,
548 					 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
549 	{
550 		std::vector<std::tuple<std::string, macro_handler_s, void *>> emptyVector;
551 		return parse(in, replacer, emptyVector, err, duplicate_strategy);
552 	}
553 
554 	//Macro handler will receive a macro_userdata_s as void *ud
555 	static Ucl parse(const std::string &in, const variable_replacer &replacer,
556 					 std::vector<std::tuple<std::string /*name*/, macro_handler_s, void * /*userdata*/>> &macros,
557 					 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
558 	{
559 		//Preserve macro_userdata_s memory for later use in parse_with_strategy_function()
560 		std::vector<macro_userdata_s> userdata_list;
561 		userdata_list.reserve(macros.size());
562 		auto config_func = [&userdata_list, &replacer, &macros](ucl_parser *parser) {
563 			ucl_parser_set_variables_handler(parser, ucl_variable_replacer, &const_cast<variable_replacer &>(replacer));
564 			for (auto &macro: macros) {
565 				userdata_list.push_back({parser, std::get<2>(macro)});
566 				if (std::get<1>(macro).handler != NULL) {
567 					ucl_parser_register_macro(parser,
568 											  std::get<0>(macro).c_str(),
569 											  std::get<1>(macro).handler,
570 											  reinterpret_cast<void *>(&userdata_list.back()));
571 				}
572 				else if (std::get<1>(macro).ctx_handler != NULL) {
573 					ucl_parser_register_context_macro(parser,
574 													  std::get<0>(macro).c_str(),
575 													  std::get<1>(macro).ctx_handler,
576 													  reinterpret_cast<void *>(&userdata_list.back()));
577 				}
578 			}
579 		};
580 
581 		auto parse_func = [&in, &duplicate_strategy](struct ucl_parser *parser) -> bool {
582 			return ucl_parser_add_chunk_full(parser,
583 											 (unsigned char *) in.data(),
584 											 in.size(),
585 											 (unsigned int) ucl_parser_get_default_priority(parser),
586 											 duplicate_strategy,
587 											 UCL_PARSE_UCL);
588 		};
589 
590 		return parse_with_strategy_function(config_func, parse_func, err);
591 	}
592 
593 	static Ucl parse(const char *in, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
594 	{
595 		return parse(in, std::map<std::string, std::string>(), err, duplicate_strategy);
596 	}
597 
598 	static Ucl parse(const char *in, const std::map<std::string, std::string> &vars, std::string &err)
599 	{
600 		if (!in) {
601 			err = "null input";
602 			return nullptr;
603 		}
604 		return parse(std::string(in), vars, err);
605 	}
606 
607 	//Macro handler will receive a macro_userdata_s as void *ud
608 	static Ucl parse(const char *in,
609 					 std::vector<std::tuple<std::string /*name*/, macro_handler_s, void * /*userdata*/>> &macros,
610 					 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
611 	{
612 		return parse(in, std::map<std::string, std::string>(), macros, err, duplicate_strategy);
613 	}
614 
615 	//Macro handler will receive a macro_userdata_s as void *ud
616 	static Ucl parse(const char *in, const std::map<std::string, std::string> &vars,
617 					 std::vector<std::tuple<std::string /*name*/, macro_handler_s, void * /*userdata*/>> &macros,
618 					 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
619 	{
620 		if (!in) {
621 			err = "null input";
622 			return nullptr;
623 		}
624 		return parse(std::string(in), vars, macros, err, duplicate_strategy);
625 	}
626 
627 	static Ucl parse(const char *in, const variable_replacer &replacer,
628 					 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
629 	{
630 		if (!in) {
631 			err = "null input";
632 			return nullptr;
633 		}
634 		return parse(std::string(in), replacer, err, duplicate_strategy);
635 	}
636 
637 	//Macro handler will receive a macro_userdata_s as void *ud
638 	static Ucl parse(const char *in, const variable_replacer &replacer,
639 					 std::vector<std::tuple<std::string /*name*/, macro_handler_s, void * /*userdata*/>> &macros,
640 					 std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
641 	{
642 		if (!in) {
643 			err = "null input";
644 			return nullptr;
645 		}
646 		return parse(std::string(in), replacer, macros, err, duplicate_strategy);
647 	}
648 
649 	static Ucl parse_from_file(const std::string &filename, std::string &err)
650 	{
651 		return parse_from_file(filename, std::map<std::string, std::string>(), err);
652 	}
653 
654 	static Ucl parse_from_file(const std::string &filename, const std::map<std::string, std::string> &vars, std::string &err)
655 	{
656 		auto config_func = [&vars](ucl_parser *parser) {
657 			for (const auto &item: vars) {
658 				ucl_parser_register_variable(parser, item.first.c_str(), item.second.c_str());
659 			}
660 		};
661 
662 		auto parse_func = [&filename](ucl_parser *parser) {
663 			return ucl_parser_add_file(parser, filename.c_str());
664 		};
665 
666 		return parse_with_strategy_function(config_func, parse_func, err);
667 	}
668 
669 	static Ucl parse_from_file(const std::string &filename, const variable_replacer &replacer, std::string &err)
670 	{
671 		auto config_func = [&replacer](ucl_parser *parser) {
672 			ucl_parser_set_variables_handler(parser, ucl_variable_replacer,
673 											 &const_cast<variable_replacer &>(replacer));
674 		};
675 
676 		auto parse_func = [&filename](ucl_parser *parser) {
677 			return ucl_parser_add_file(parser, filename.c_str());
678 		};
679 
680 		return parse_with_strategy_function(config_func, parse_func, err);
681 	}
682 
683 	static std::vector<std::string> find_variable(const std::string &in)
684 	{
685 		auto parser = ucl_parser_new(UCL_PARSER_DEFAULT);
686 
687 		std::set<std::string> vars;
688 		ucl_parser_set_variables_handler(parser, ucl_variable_getter, &vars);
689 		ucl_parser_add_chunk(parser, (const unsigned char *) in.data(), in.size());
690 		ucl_parser_free(parser);
691 
692 		std::vector<std::string> result;
693 		std::move(vars.begin(), vars.end(), std::back_inserter(result));
694 		return result;
695 	}
696 
697 	static std::vector<std::string> find_variable(const char *in)
698 	{
699 		if (!in) {
700 			return std::vector<std::string>();
701 		}
702 		return find_variable(std::string(in));
703 	}
704 
705 	static std::vector<std::string> find_variable_from_file(const std::string &filename)
706 	{
707 		auto parser = ucl_parser_new(UCL_PARSER_DEFAULT);
708 
709 		std::set<std::string> vars;
710 		ucl_parser_set_variables_handler(parser, ucl_variable_getter, &vars);
711 		ucl_parser_add_file(parser, filename.c_str());
712 		ucl_parser_free(parser);
713 
714 		std::vector<std::string> result;
715 		std::move(vars.begin(), vars.end(), std::back_inserter(result));
716 		return result;
717 	}
718 
719 	Ucl &operator=(Ucl rhs)
720 	{
721 		obj.swap(rhs.obj);
722 		return *this;
723 	}
724 
725 	bool operator==(const Ucl &rhs) const
726 	{
727 		return ucl_object_compare(obj.get(), rhs.obj.get()) == 0;
728 	}
729 	bool operator<(const Ucl &rhs) const
730 	{
731 		return ucl_object_compare(obj.get(), rhs.obj.get()) < 0;
732 	}
733 	bool operator!=(const Ucl &rhs) const
734 	{
735 		return !(*this == rhs);
736 	}
737 	bool operator<=(const Ucl &rhs) const
738 	{
739 		return !(rhs < *this);
740 	}
741 	bool operator>(const Ucl &rhs) const
742 	{
743 		return (rhs < *this);
744 	}
745 	bool operator>=(const Ucl &rhs) const
746 	{
747 		return !(*this < rhs);
748 	}
749 
750 	explicit operator bool() const
751 	{
752 		if (!obj || type() == UCL_NULL) {
753 			return false;
754 		}
755 
756 		if (type() == UCL_BOOLEAN) {
757 			return bool_value();
758 		}
759 
760 		return true;
761 	}
762 
763 	const_iterator begin() const
764 	{
765 		return const_iterator(*this);
766 	}
767 	const_iterator cbegin() const
768 	{
769 		return const_iterator(*this);
770 	}
771 	const_iterator end() const
772 	{
773 		return const_iterator();
774 	}
775 	const_iterator cend() const
776 	{
777 		return const_iterator();
778 	}
779 };
780 
781 };// namespace ucl
782