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
37namespace ucl {
38
39struct ucl_map_construct_t { };
40constexpr ucl_map_construct_t ucl_map_construct = ucl_map_construct_t();
41struct ucl_array_construct_t { };
42constexpr ucl_array_construct_t ucl_array_construct = ucl_array_construct_t();
43
44class Ucl final {
45private:
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
170public:
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