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