xref: /freebsd/contrib/libucl/include/ucl++.h (revision fed1ca4b719c56c930f2259d80663cd34be812bb)
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 <memory>
28 #include <iostream>
29 
30 #include "ucl.h"
31 
32 // C++11 API inspired by json11: https://github.com/dropbox/json11/
33 
34 namespace ucl {
35 
36 struct ucl_map_construct_t { };
37 constexpr ucl_map_construct_t ucl_map_construct = ucl_map_construct_t();
38 struct ucl_array_construct_t { };
39 constexpr ucl_array_construct_t ucl_array_construct = ucl_array_construct_t();
40 
41 class Ucl final {
42 private:
43 
44 	struct ucl_deleter {
45 		void operator() (ucl_object_t *obj) {
46 			ucl_object_unref (obj);
47 		}
48 	};
49 
50 	static int
51 	append_char (unsigned char c, size_t nchars, void *ud)
52 	{
53 		std::string *out = reinterpret_cast<std::string *>(ud);
54 
55 		out->append (nchars, (char)c);
56 
57 		return nchars;
58 	}
59 	static int
60 	append_len (unsigned const char *str, size_t len, void *ud)
61 	{
62 		std::string *out = reinterpret_cast<std::string *>(ud);
63 
64 		out->append ((const char *)str, len);
65 
66 		return len;
67 	}
68 	static int
69 	append_int (int64_t elt, void *ud)
70 	{
71 		std::string *out = reinterpret_cast<std::string *>(ud);
72 		auto nstr = std::to_string (elt);
73 
74 		out->append (nstr);
75 
76 		return nstr.size ();
77 	}
78 	static int
79 	append_double (double elt, void *ud)
80 	{
81 		std::string *out = reinterpret_cast<std::string *>(ud);
82 		auto nstr = std::to_string (elt);
83 
84 		out->append (nstr);
85 
86 		return nstr.size ();
87 	}
88 
89 	static struct ucl_emitter_functions default_emit_funcs()
90 	{
91 		struct ucl_emitter_functions func = {
92 			Ucl::append_char,
93 			Ucl::append_len,
94 			Ucl::append_int,
95 			Ucl::append_double,
96 			nullptr,
97 			nullptr
98 		};
99 
100 		return func;
101 	};
102 
103 	std::unique_ptr<ucl_object_t, ucl_deleter> obj;
104 
105 public:
106 	class const_iterator {
107 	private:
108 		struct ucl_iter_deleter {
109 			void operator() (ucl_object_iter_t it) {
110 				ucl_object_iterate_free (it);
111 			}
112 		};
113 		std::shared_ptr<void> it;
114 		std::unique_ptr<Ucl> cur;
115 	public:
116 		typedef std::forward_iterator_tag iterator_category;
117 
118 		const_iterator(const Ucl &obj) {
119 			it = std::shared_ptr<void>(ucl_object_iterate_new (obj.obj.get()),
120 					ucl_iter_deleter());
121 			cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true)));
122 			if (!*cur) {
123 				it.reset ();
124 				cur.reset ();
125 			}
126 		}
127 
128 		const_iterator() {}
129 		const_iterator(const const_iterator &other) = delete;
130 		const_iterator(const_iterator &&other) = default;
131 		~const_iterator() {}
132 
133 		const_iterator& operator=(const const_iterator &other) = delete;
134 		const_iterator& operator=(const_iterator &&other) = default;
135 
136 		bool operator==(const const_iterator &other) const
137 		{
138 			if (cur && other.cur) {
139 				return cur->obj.get() == other.cur->obj.get();
140 			}
141 
142 			return !cur && !other.cur;
143 		}
144 
145 		bool operator!=(const const_iterator &other) const
146 		{
147 			return !(*this == other);
148 		}
149 
150 		const_iterator& operator++()
151 		{
152 			if (it) {
153 				cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true)));
154 			}
155 
156 			if (!*cur) {
157 				it.reset ();
158 				cur.reset ();
159 			}
160 
161 			return *this;
162 		}
163 
164 		const Ucl& operator*() const
165 		{
166 			return *cur;
167 		}
168 		const Ucl* operator->() const
169 		{
170 			return cur.get();
171 		}
172 	};
173 
174 	// We grab ownership if get non-const ucl_object_t
175 	Ucl(ucl_object_t *other) {
176 		obj.reset (other);
177 	}
178 
179 	// Shared ownership
180 	Ucl(const ucl_object_t *other) {
181 		obj.reset (ucl_object_ref (other));
182 	}
183 
184 	Ucl(const Ucl &other) {
185 		obj.reset (ucl_object_ref (other.obj.get()));
186 	}
187 
188 	Ucl(Ucl &&other) {
189 		obj.swap (other.obj);
190 	}
191 
192 	Ucl() noexcept {
193 		obj.reset (ucl_object_typed_new (UCL_NULL));
194 	}
195 	Ucl(std::nullptr_t) noexcept {
196 		obj.reset (ucl_object_typed_new (UCL_NULL));
197 	}
198 	Ucl(double value) {
199 		obj.reset (ucl_object_typed_new (UCL_FLOAT));
200 		obj->value.dv = value;
201 	}
202 	Ucl(int64_t value) {
203 		obj.reset (ucl_object_typed_new (UCL_INT));
204 		obj->value.iv = value;
205 	}
206 	Ucl(bool value) {
207 		obj.reset (ucl_object_typed_new (UCL_BOOLEAN));
208 		obj->value.iv = static_cast<int64_t>(value);
209 	}
210 	Ucl(const std::string &value) {
211 		obj.reset (ucl_object_fromstring_common (value.data (), value.size (),
212 				UCL_STRING_RAW));
213 	}
214 	Ucl(const char * value) {
215 		obj.reset (ucl_object_fromstring_common (value, 0, UCL_STRING_RAW));
216 	}
217 
218 	// Implicit constructor: anything with a to_json() function.
219 	template <class T, class = decltype(&T::to_ucl)>
220 	Ucl(const T & t) : Ucl(t.to_ucl()) {}
221 
222 	// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
223 	template <class M, typename std::enable_if<
224 		std::is_constructible<std::string, typename M::key_type>::value
225 		&& std::is_constructible<Ucl, typename M::mapped_type>::value,
226 		int>::type = 0>
227 	Ucl(const M & m) {
228 		obj.reset (ucl_object_typed_new (UCL_OBJECT));
229 		auto cobj = obj.get ();
230 
231 		for (const auto &e : m) {
232 			ucl_object_insert_key (cobj, ucl_object_ref (e.second.obj.get()),
233 					e.first.data (), e.first.size (), true);
234 		}
235 	}
236 
237 	// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
238 	template <class V, typename std::enable_if<
239 		std::is_constructible<Ucl, typename V::value_type>::value,
240 		int>::type = 0>
241 	Ucl(const V & v) {
242 		obj.reset (ucl_object_typed_new (UCL_ARRAY));
243 		auto cobj = obj.get ();
244 
245 		for (const auto &e : v) {
246 			ucl_array_append (cobj, ucl_object_ref (e.obj.get()));
247 		}
248 	}
249 
250 	ucl_type_t type () const {
251 		if (obj) {
252 			return ucl_object_type (obj.get ());
253 		}
254 		return UCL_NULL;
255 	}
256 
257 	const std::string key () const {
258 		std::string res;
259 
260 		if (obj->key) {
261 			res.assign (obj->key, obj->keylen);
262 		}
263 
264 		return res;
265 	}
266 
267 	double number_value (const double default_val = 0.0) const
268 	{
269 		double res;
270 
271 		if (ucl_object_todouble_safe(obj.get(), &res)) {
272 			return res;
273 		}
274 
275 		return default_val;
276 	}
277 
278 	int64_t int_value (const int64_t default_val = 0) const
279 	{
280 		int64_t res;
281 
282 		if (ucl_object_toint_safe(obj.get(), &res)) {
283 			return res;
284 		}
285 
286 		return default_val;
287 	}
288 
289 	bool bool_value (const bool default_val = false) const
290 	{
291 		bool res;
292 
293 		if (ucl_object_toboolean_safe(obj.get(), &res)) {
294 			return res;
295 		}
296 
297 		return default_val;
298 	}
299 
300 	const std::string string_value (const std::string& default_val = "") const
301 	{
302 		const char* res = nullptr;
303 
304 		if (ucl_object_tostring_safe(obj.get(), &res)) {
305 			return res;
306 		}
307 
308 		return default_val;
309 	}
310 
311 	const Ucl at (size_t i) const
312 	{
313 		if (type () == UCL_ARRAY) {
314 			return Ucl (ucl_array_find_index (obj.get(), i));
315 		}
316 
317 		return Ucl (nullptr);
318 	}
319 
320 	const Ucl lookup (const std::string &key) const
321 	{
322 		if (type () == UCL_OBJECT) {
323 			return Ucl (ucl_object_lookup_len (obj.get(),
324 					key.data (), key.size ()));
325 		}
326 
327 		return Ucl (nullptr);
328 	}
329 
330 	inline const Ucl operator[] (size_t i) const
331 	{
332 		return at(i);
333 	}
334 
335 	inline const Ucl operator[](const std::string &key) const
336 	{
337 		return lookup(key);
338 	}
339 	// Serialize.
340 	void dump (std::string &out, ucl_emitter_t type = UCL_EMIT_JSON) const
341 	{
342 		struct ucl_emitter_functions cbdata;
343 
344 		cbdata = Ucl::default_emit_funcs();
345 		cbdata.ud = reinterpret_cast<void *>(&out);
346 
347 		ucl_object_emit_full (obj.get(), type, &cbdata, nullptr);
348 	}
349 
350 	std::string dump (ucl_emitter_t type = UCL_EMIT_JSON) const
351 	{
352 		std::string out;
353 
354 		dump (out, type);
355 
356 		return out;
357 	}
358 
359 	static Ucl parse (const std::string & in, std::string & err)
360 	{
361 		auto parser = ucl_parser_new (UCL_PARSER_DEFAULT);
362 
363 		if (!ucl_parser_add_chunk (parser, (const unsigned char *)in.data (),
364 				in.size ())) {
365 			err.assign (ucl_parser_get_error (parser));
366 			ucl_parser_free (parser);
367 
368 			return nullptr;
369 		}
370 
371 		auto obj = ucl_parser_get_object (parser);
372 		ucl_parser_free (parser);
373 
374 		// Obj will handle ownership
375 		return Ucl (obj);
376 	}
377 
378 	static Ucl parse (const char * in, std::string & err)
379 	{
380 		if (in) {
381 			return parse (std::string(in), err);
382 		} else {
383 			err = "null input";
384 			return nullptr;
385 		}
386 	}
387 
388 	static Ucl parse (std::istream &ifs, std::string &err)
389 	{
390 		return Ucl::parse (std::string(std::istreambuf_iterator<char>(ifs),
391 				std::istreambuf_iterator<char>()), err);
392 	}
393 
394     Ucl& operator= (Ucl rhs)
395     {
396         obj.swap (rhs.obj);
397         return *this;
398     }
399 
400 	bool operator== (const Ucl &rhs) const
401 	{
402 		return ucl_object_compare (obj.get(), rhs.obj.get ()) == 0;
403 	}
404 	bool operator< (const Ucl &rhs) const
405 	{
406 		return ucl_object_compare (obj.get(), rhs.obj.get ()) < 0;
407 	}
408 	bool operator!= (const Ucl &rhs) const { return !(*this == rhs); }
409 	bool operator<= (const Ucl &rhs) const { return !(rhs < *this); }
410 	bool operator> (const Ucl &rhs) const { return (rhs < *this); }
411 	bool operator>= (const Ucl &rhs) const { return !(*this < rhs); }
412 
413 	explicit operator bool () const
414 	{
415 		if (!obj || type() == UCL_NULL) {
416 			return false;
417 		}
418 
419 		if (type () == UCL_BOOLEAN) {
420 			return bool_value ();
421 		}
422 
423 		return true;
424 	}
425 
426 	const_iterator begin() const
427 	{
428 		return const_iterator(*this);
429 	}
430 	const_iterator cbegin() const
431 	{
432 		return const_iterator(*this);
433 	}
434 	const_iterator end() const
435 	{
436 		return const_iterator();
437 	}
438 	const_iterator cend() const
439 	{
440 		return const_iterator();
441 	}
442 };
443 
444 };
445