xref: /freebsd/contrib/libucl/include/ucl++.h (revision 39ee7a7a6bdd1557b1c3532abf60d139798ac88b)
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 #include <strstream>
30 
31 #include "ucl.h"
32 
33 // C++11 API inspired by json11: https://github.com/dropbox/json11/
34 
35 namespace ucl {
36 
37 struct ucl_map_construct_t { };
38 constexpr ucl_map_construct_t ucl_map_construct = ucl_map_construct_t();
39 struct ucl_array_construct_t { };
40 constexpr ucl_array_construct_t ucl_array_construct = ucl_array_construct_t();
41 
42 class Ucl final {
43 private:
44 
45 	struct ucl_deleter {
46 		void operator() (ucl_object_t *obj) {
47 			ucl_object_unref (obj);
48 		}
49 	};
50 
51 	static int
52 	append_char (unsigned char c, size_t nchars, void *ud)
53 	{
54 		std::string *out = reinterpret_cast<std::string *>(ud);
55 
56 		out->append (nchars, (char)c);
57 
58 		return nchars;
59 	}
60 	static int
61 	append_len (unsigned const char *str, size_t len, void *ud)
62 	{
63 		std::string *out = reinterpret_cast<std::string *>(ud);
64 
65 		out->append ((const char *)str, len);
66 
67 		return len;
68 	}
69 	static int
70 	append_int (int64_t elt, void *ud)
71 	{
72 		std::string *out = reinterpret_cast<std::string *>(ud);
73 		auto nstr = std::to_string (elt);
74 
75 		out->append (nstr);
76 
77 		return nstr.size ();
78 	}
79 	static int
80 	append_double (double elt, void *ud)
81 	{
82 		std::string *out = reinterpret_cast<std::string *>(ud);
83 		auto nstr = std::to_string (elt);
84 
85 		out->append (nstr);
86 
87 		return nstr.size ();
88 	}
89 
90 	static struct ucl_emitter_functions default_emit_funcs()
91 	{
92 		struct ucl_emitter_functions func = {
93 			Ucl::append_char,
94 			Ucl::append_len,
95 			Ucl::append_int,
96 			Ucl::append_double,
97 			nullptr,
98 			nullptr
99 		};
100 
101 		return func;
102 	};
103 
104 	std::unique_ptr<ucl_object_t, ucl_deleter> obj;
105 
106 public:
107 	class const_iterator {
108 	private:
109 		struct ucl_iter_deleter {
110 			void operator() (ucl_object_iter_t it) {
111 				ucl_object_iterate_free (it);
112 			}
113 		};
114 		std::shared_ptr<void> it;
115 		std::unique_ptr<Ucl> cur;
116 	public:
117 		typedef std::forward_iterator_tag iterator_category;
118 
119 		const_iterator(const Ucl &obj) {
120 			it = std::shared_ptr<void>(ucl_object_iterate_new (obj.obj.get()),
121 					ucl_iter_deleter());
122 			cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true)));
123 		}
124 
125 		const_iterator() {}
126 		const_iterator(const const_iterator &other) {
127 			it = other.it;
128 		}
129 		~const_iterator() {}
130 
131 		const_iterator& operator=(const const_iterator &other) {
132 			it = other.it;
133 			return *this;
134 		}
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
268 	{
269 		if (obj) {
270 			return ucl_object_todouble (obj.get());
271 		}
272 
273 		return 0.0;
274 	}
275 
276 	int64_t int_value () const
277 	{
278 		if (obj) {
279 			return ucl_object_toint (obj.get());
280 		}
281 
282 		return 0;
283 	}
284 
285 	bool bool_value () const
286 	{
287 		if (obj) {
288 			return ucl_object_toboolean (obj.get());
289 		}
290 
291 		return false;
292 	}
293 
294 	const std::string string_value () const
295 	{
296 		std::string res;
297 
298 		if (obj) {
299 			res.assign (ucl_object_tostring (obj.get()));
300 		}
301 
302 		return res;
303 	}
304 
305 	const Ucl operator[] (size_t i) const
306 	{
307 		if (type () == UCL_ARRAY) {
308 			return Ucl (ucl_array_find_index (obj.get(), i));
309 		}
310 
311 		return Ucl (nullptr);
312 	}
313 
314 	const Ucl operator[](const std::string &key) const
315 	{
316 		if (type () == UCL_OBJECT) {
317 			return Ucl (ucl_object_find_keyl (obj.get(),
318 					key.data (), key.size ()));
319 		}
320 
321 		return Ucl (nullptr);
322 	}
323 	// Serialize.
324 	void dump (std::string &out, ucl_emitter_t type = UCL_EMIT_JSON) const
325 	{
326 		struct ucl_emitter_functions cbdata;
327 
328 		cbdata = Ucl::default_emit_funcs();
329 		cbdata.ud = reinterpret_cast<void *>(&out);
330 
331 		ucl_object_emit_full (obj.get(), type, &cbdata);
332 	}
333 
334 	std::string dump (ucl_emitter_t type = UCL_EMIT_JSON) const
335 	{
336 		std::string out;
337 
338 		dump (out, type);
339 
340 		return out;
341 	}
342 
343 	static Ucl parse (const std::string & in, std::string & err)
344 	{
345 		auto parser = ucl_parser_new (UCL_PARSER_DEFAULT);
346 
347 		if (!ucl_parser_add_chunk (parser, (const unsigned char *)in.data (),
348 				in.size ())) {
349 			err.assign (ucl_parser_get_error (parser));
350 			ucl_parser_free (parser);
351 
352 			return nullptr;
353 		}
354 
355 		auto obj = ucl_parser_get_object (parser);
356 		ucl_parser_free (parser);
357 
358 		// Obj will handle ownership
359 		return Ucl (obj);
360 	}
361 
362 	static Ucl parse (const char * in, std::string & err)
363 	{
364 		if (in) {
365 			return parse (std::string(in), err);
366 		} else {
367 			err = "null input";
368 			return nullptr;
369 		}
370 	}
371 
372 	static Ucl parse (std::istream &ifs, std::string &err)
373 	{
374 		return Ucl::parse (std::string(std::istreambuf_iterator<char>(ifs),
375 				std::istreambuf_iterator<char>()), err);
376 	}
377 
378 	bool operator== (const Ucl &rhs) const
379 	{
380 		return ucl_object_compare (obj.get(), rhs.obj.get ()) == 0;
381 	}
382 	bool operator< (const Ucl &rhs) const
383 	{
384 		return ucl_object_compare (obj.get(), rhs.obj.get ()) < 0;
385 	}
386 	bool operator!= (const Ucl &rhs) const { return !(*this == rhs); }
387 	bool operator<= (const Ucl &rhs) const { return !(rhs < *this); }
388 	bool operator> (const Ucl &rhs) const { return (rhs < *this); }
389 	bool operator>= (const Ucl &rhs) const { return !(*this < rhs); }
390 
391 	operator bool () const
392 	{
393 		if (!obj || type() == UCL_NULL) {
394 			return false;
395 		}
396 
397 		if (type () == UCL_BOOLEAN) {
398 			return bool_value ();
399 		}
400 
401 		return true;
402 	}
403 
404 	const_iterator begin() const
405 	{
406 		return const_iterator(*this);
407 	}
408 	const_iterator cbegin() const
409 	{
410 		return const_iterator(*this);
411 	}
412 	const_iterator end() const
413 	{
414 		return const_iterator();
415 	}
416 	const_iterator cend() const
417 	{
418 		return const_iterator();
419 	}
420 };
421 
422 };
423