changeset 163:557b0e318d20

Irccd: update json module
author David Demelier <markand@malikania.fr>
date Wed, 25 May 2016 20:56:48 +0200
parents a24e2de22565
children 3b38931801ff
files lib/irccd/connection.hpp lib/irccd/json.cpp lib/irccd/json.hpp lib/irccd/transport-client.cpp
diffstat 4 files changed, 289 insertions(+), 261 deletions(-) [+]
line wrap: on
line diff
--- a/lib/irccd/connection.hpp	Tue May 24 17:11:49 2016 +0200
+++ b/lib/irccd/connection.hpp	Wed May 25 20:56:48 2016 +0200
@@ -233,7 +233,7 @@
 	if (!isConnected())
 		throw std::runtime_error("connection lost");
 
-	json::Value value(json::Buffer{buffer});
+	json::Value value = json::fromString(buffer);
 
 	if (!value.isObject())
 		throw std::invalid_argument("invalid message received");
--- a/lib/irccd/json.cpp	Tue May 24 17:11:49 2016 +0200
+++ b/lib/irccd/json.cpp	Wed May 25 20:56:48 2016 +0200
@@ -1,7 +1,7 @@
 /*
- * json.cpp -- C++14 JSON manipulation using jansson parser
+ * json.cpp -- C++14 JSON manipulation using jansson
  *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ * Copyright (c) 2015-2016 David Demelier <markand@malikania.fr>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -217,31 +217,7 @@
 	}
 }
 
-bool Value::toBool() const noexcept
-{
-	if (m_type != Type::Boolean)
-		return false;
-
-	return m_boolean;
-}
-
-double Value::toReal() const noexcept
-{
-	if (m_type != Type::Real)
-		return 0;
-
-	return m_number;
-}
-
-int Value::toInt() const noexcept
-{
-	if (m_type != Type::Int)
-		return 0;
-
-	return m_integer;
-}
-
-std::string Value::toString(bool coerce) const noexcept
+std::string Value::toString(bool coerce) const
 {
 	std::string result;
 
@@ -253,16 +229,6 @@
 	return result;
 }
 
-Value::Value(const Buffer &buffer)
-{
-	*this = convert(json_loads, buffer.text.c_str(), 0);
-}
-
-Value::Value(const File &file)
-{
-	*this = convert(json_load_file, file.path.c_str(), 0);
-}
-
 std::string Value::toJson(int level, int current) const
 {
 	std::ostringstream oss;
@@ -299,13 +265,13 @@
 		for (const auto &pair : m_object) {
 			oss << indent(level, current + 1);
 
-			/* Key and : */
+			// Key and :.
 			oss << "\"" << pair.first << "\":" << (level != 0 ? " " : "");
 
-			/* Value */
+			// Value.
 			oss << pair.second.toJson(level, current + 1);
 
-			/* Comma, new line if needed */
+			// Comma, new line if needed.
 			oss << (++i < total ? "," : "") << (level != 0 ? "\n" : "");
 		}
 
@@ -364,6 +330,16 @@
 	return result;
 }
 
+Value fromString(const std::string &buffer)
+{
+	return convert(json_loads, buffer.c_str(), 0);
+}
+
+Value fromFile(const std::string &path)
+{
+	return convert(json_load_file, path.c_str(), 0);
+}
+
 } // !json
 
 } // !irccd
--- a/lib/irccd/json.hpp	Tue May 24 17:11:49 2016 +0200
+++ b/lib/irccd/json.hpp	Wed May 25 20:56:48 2016 +0200
@@ -1,7 +1,7 @@
 /*
- * json.hpp -- C++14 JSON manipulation using jansson parser
+ * json.hpp -- C++14 JSON manipulation using jansson
  *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ * Copyright (c) 2015-2016 David Demelier <markand@malikania.fr>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -16,16 +16,49 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#ifndef IRCCD_JSON_HPP
-#define IRCCD_JSON_HPP
+#ifndef JSON_HPP
+#define JSON_HPP
 
 /**
  * \file json.hpp
- * \brief Jansson C++14 wrapper
+ * \brief C++14 JSON manipulation using jansson.
+ * \author David Demelier <markand@malikania.fr>
+ */
+
+/**
+ * \page Json Json
+ * \brief C++14 JSON manipulation using jansson.
+ *
+ * This library uses Jansson for parsing files only. It then converts the structure into internal storage.
+ *
+ * ## Creating objects and arrays
+ *
+ * The following code shows how you can easily create objects or arrays.
  *
- * These classes can be used to build or parse JSON documents using jansson library. It is designed to be safe
- * and explicit. It does not implement implicit sharing like jansson so when you access (e.g. Value::toObject) values
- * you get real copies, thus when you read big documents it can has a performance cost.
+ * ````cpp
+ * auto object = json::object({
+ *   { "x", 1 },
+ *   { "y", 2 }
+ * });
+ *
+ * auto array = json::array({ 123, 456, "hello" });
+ * ````
+ *
+ * ## Reading sources
+ *
+ * You can use json::fromFile and json::fromString to load JSON documents.
+ *
+ * ````cpp
+ * #include <iostream>
+ *
+ * #include "json.h"
+ *
+ * try {
+ *   auto value = json::fromFile("foo.json");
+ * } catch (const std::exception &ex) {
+ *   std::cerr << ex.what() << std::endl;
+ * }
+ * ````
  */
 
 #include <cassert>
@@ -36,8 +69,6 @@
 #include <utility>
 #include <vector>
 
-#include "sysconfig.hpp"
-
 namespace irccd {
 
 /**
@@ -152,21 +183,166 @@
 };
 
 /**
- * \class Buffer
- * \brief Open JSON document from text.
+ * \class Iterator
+ * \brief This is the base class for iterator and const_iterator
+ *
+ * This iterator works for both arrays and objects. Because of that purpose, it is only available
+ * as forward iterator.
+ *
+ * When iterator comes from an object, you can use key() otherwise you can use index().
  */
-class Buffer {
+template <typename ValueType, typename ArrayIteratorType, typename ObjectIteratorType>
+class Iterator : public std::iterator<std::forward_iterator_tag, ValueType> {
+private:
+	friend class Value;
+
+	ValueType *m_parent{nullptr};
+	ArrayIteratorType m_ita;
+	ObjectIteratorType m_itm;
+
+	inline void increment()
+	{
+		if (m_parent->isObject())
+			m_itm++;
+		else
+			m_ita++;
+	}
+
+	inline Iterator(ValueType *parent, ObjectIteratorType it)
+		: m_parent(parent)
+		, m_itm(it)
+	{
+		assert(parent);
+	}
+
+	inline Iterator(ValueType *parent, ArrayIteratorType it)
+		: m_parent(parent)
+		, m_ita(it)
+	{
+		assert(parent);
+	}
+
 public:
-	std::string text;	//!< The JSON text
-};
+	/**
+	 * Default constructor.
+	 */
+	Iterator() = default;
+
+	/**
+	 * Get the iterator key (for objects).
+	 *
+	 * \pre iterator must be dereferenceable
+	 * \pre iterator must come from object
+	 * \return the key
+	 */
+	inline const std::string &key() const noexcept
+	{
+		assert(m_parent && m_parent->isObject());
+		assert(m_itm != m_parent->m_object.end());
+
+		return m_itm->first;
+	}
+
+	/**
+	 * Get the iterator position (for arrays).
+	 *
+	 * \pre iterator must be dereferenceable
+	 * \pre iterator must come from arrays
+	 * \return the index
+	 */
+	inline unsigned index() const noexcept
+	{
+		assert(m_parent && m_parent->isArray());
+		assert(m_ita != m_parent->m_array.end());
+
+		return std::distance(m_parent->m_array.begin(), m_ita);
+	}
 
-/**
- * \class File
- * \brief Open JSON document from a file.
- */
-class File {
-public:
-	std::string path;	//!< The path to the file
+	/**
+	 * Dereference the iterator.
+	 *
+	 * \pre iterator be dereferenceable
+	 * \return the value
+	 */
+	inline ValueType &operator*() noexcept
+	{
+		assert(m_parent);
+		assert((m_parent->isArray()  && m_ita != m_parent->m_array.end()) ||
+		       (m_parent->isObject() && m_itm != m_parent->m_object.end()));
+
+		return (m_parent->m_type == Type::Object) ? m_itm->second : *m_ita;
+	}
+
+	/**
+	 * Dereference the iterator as a pointer.
+	 *
+	 * \pre iterator must be dereferenceable
+	 * \return the value
+	 */
+	inline ValueType *operator->() noexcept
+	{
+		assert(m_parent);
+		assert((m_parent->isArray()  && m_ita != m_parent->m_array.end()) ||
+		       (m_parent->isObject() && m_itm != m_parent->m_object.end()));
+
+		return (m_parent->m_type == Type::Object) ? &m_itm->second : &(*m_ita);
+	}
+
+	/**
+	 * Increment the iterator. (Prefix version).
+	 *
+	 * \pre iterator must be dereferenceable
+	 * \return *this;
+	 */
+	inline Iterator &operator++() noexcept
+	{
+		assert(m_parent);
+		assert((m_parent->isArray()  && m_ita != m_parent->m_array.end()) ||
+		       (m_parent->isObject() && m_itm != m_parent->m_object.end()));
+
+		increment();
+
+		return *this;
+	}
+
+	/**
+	 * Increment the iterator. (Postfix version).
+	 *
+	 * \pre iterator must be dereferenceable
+	 * \return *this;
+	 */
+	inline Iterator &operator++(int) noexcept
+	{
+		assert(m_parent);
+		assert((m_parent->isArray()  && m_ita != m_parent->m_array.end()) ||
+		       (m_parent->isObject() && m_itm != m_parent->m_object.end()));
+
+		increment();
+
+		return *this;
+	}
+
+	/**
+	 * Compare two iterators.
+	 *
+	 * \param it the first iterator
+	 * \return true if they are same
+	 */
+	bool operator==(const Iterator &it) const noexcept
+	{
+		return m_parent == it.m_parent && m_itm == it.m_itm && m_ita == it.m_ita;
+	}
+
+	/**
+	 * Test if the iterator is different.
+	 *
+	 * \param it the iterator
+	 * \return true if they are different
+	 */
+	inline bool operator!=(const Iterator &it) const noexcept
+	{
+		return !(*this == it);
+	}
 };
 
 /**
@@ -190,179 +366,24 @@
 	void move(Value &&);
 	std::string toJson(int indent, int current) const;
 
-	/**
-	 * \class BaseIterator
-	 * \brief This is the base class for iterator and const_iterator
-	 *
-	 * This iterator works for both arrays and objects. Because of that purpose, it is only available
-	 * as forward iterator.
-	 *
-	 * When iterator comes from an object, you can use key() otherwise you can use index().
-	 */
-	template <typename ValueType, typename ArrayIteratorType, typename ObjectIteratorType>
-	class BaseIterator : public std::iterator<std::forward_iterator_tag, ValueType> {
-	private:
-		friend class Value;
-
-		ValueType &m_value;
-		ArrayIteratorType m_ita;
-		ObjectIteratorType m_itm;
-
-		inline void increment()
-		{
-			if (m_value.isObject())
-				m_itm++;
-			else
-				m_ita++;
-		}
-
-		BaseIterator(ValueType &value, ObjectIteratorType it)
-			: m_value(value)
-			, m_itm(it)
-		{
-		}
-
-		BaseIterator(ValueType &value, ArrayIteratorType it)
-			: m_value(value)
-			, m_ita(it)
-		{
-		}
-
-	public:
-		/**
-		 * Get the iterator key (for objects).
-		 *
-		 * \pre iterator must be dereferenceable
-		 * \pre iterator must come from object
-		 * \return the key
-		 */
-		inline const std::string &key() const noexcept
-		{
-			assert(m_value.isObject());
-			assert(m_itm != m_value.m_object.end());
-
-			return m_itm->first;
-		}
-
-		/**
-		 * Get the iterator position (for arrays).
-		 *
-		 * \pre iterator must be dereferenceable
-		 * \pre iterator must come from arrays
-		 * \return the index
-		 */
-		inline unsigned index() const noexcept
-		{
-			assert(m_value.isArray());
-			assert(m_ita != m_value.m_array.end());
-
-			return std::distance(m_value.m_array.begin(), m_ita);
-		}
-
-		/**
-		 * Dereference the iterator.
-		 *
-		 * \pre iterator be dereferenceable
-		 * \return the value
-		 */
-		inline ValueType &operator*() noexcept
-		{
-			assert((m_value.isArray()  && m_ita != m_value.m_array.end()) ||
-			       (m_value.isObject() && m_itm != m_value.m_object.end()));
-
-			return (m_value.m_type == Type::Object) ? m_itm->second : *m_ita;
-		}
-
-		/**
-		 * Dereference the iterator as a pointer.
-		 *
-		 * \pre iterator must be dereferenceable
-		 * \return the value
-		 */
-		inline ValueType *operator->() noexcept
-		{
-			assert((m_value.isArray()  && m_ita != m_value.m_array.end()) ||
-			       (m_value.isObject() && m_itm != m_value.m_object.end()));
-
-			return (m_value.m_type == Type::Object) ? &m_itm->second : &(*m_ita);
-		}
-
-		/**
-		 * Increment the iterator. (Prefix version).
-		 *
-		 * \pre iterator must be dereferenceable
-		 * \return *this;
-		 */
-		inline BaseIterator &operator++() noexcept
-		{
-			assert((m_value.isArray()  && m_ita != m_value.m_array.end()) ||
-			       (m_value.isObject() && m_itm != m_value.m_object.end()));
-
-			increment();
-
-			return *this;
-		}
-
-		/**
-		 * Increment the iterator. (Postfix version).
-		 *
-		 * \pre iterator must be dereferenceable
-		 * \return *this;
-		 */
-		inline BaseIterator &operator++(int) noexcept
-		{
-			assert((m_value.isArray()  && m_ita != m_value.m_array.end()) ||
-			       (m_value.isObject() && m_itm != m_value.m_object.end()));
-
-			increment();
-
-			return *this;
-		}
-
-		/**
-		 * Compare two iterators.
-		 *
-		 * \param it1 the first iterator
-		 * \param it2 the second iterator
-		 * \return true if they are same
-		 */
-		bool operator==(const BaseIterator &it) const noexcept
-		{
-			if (m_value.isObject() && it.m_value.isObject())
-				return m_itm == it.m_itm;
-			if (m_value.isArray() && it.m_value.isArray())
-				return m_ita == it.m_ita;
-
-			return false;
-		}
-
-		/**
-		 * Test if the iterator is different.
-		 *
-		 * \param it the iterator
-		 * \return true if they are different
-		 */
-		inline bool operator!=(const BaseIterator &it) const noexcept
-		{
-			return !(*this == it);
-		}
-	};
+	friend class Iterator<Value, typename std::vector<Value>::iterator, typename std::map<std::string, Value>::iterator>;
+	friend class Iterator<const Value, typename std::vector<Value>::const_iterator, typename std::map<std::string, Value>::const_iterator>;
 
 public:
 	/**
 	 * Forward iterator.
 	 */
-	using iterator = BaseIterator<Value, typename std::vector<Value>::iterator, typename std::map<std::string, Value>::iterator>;
+	using iterator = Iterator<Value, typename std::vector<Value>::iterator, typename std::map<std::string, Value>::iterator>;
 
 	/**
 	 * Const forward iterator.
 	 */
-	using const_iterator = BaseIterator<const Value, typename std::vector<Value>::const_iterator, typename std::map<std::string, Value>::const_iterator>;
+	using const_iterator = Iterator<const Value, typename std::vector<Value>::const_iterator, typename std::map<std::string, Value>::const_iterator>;
 
 	/**
 	 * Construct a null value.
 	 */
-	inline Value()
+	inline Value() noexcept
 	{
 	}
 
@@ -374,7 +395,7 @@
 	 *
 	 * \param type the type
 	 */
-	IRCCD_EXPORT Value(Type type);
+	Value(Type type);
 
 	/**
 	 * Construct a null value.
@@ -414,7 +435,7 @@
 	inline Value(const char *value)
 		: m_type(Type::String)
 	{
-		new (&m_string) std::string(value ? value : "");
+		new (&m_string) std::string{value ? value : ""};
 	}
 
 	/**
@@ -466,22 +487,6 @@
 	}
 
 	/**
-	 * Construct a value from a buffer.
-	 *
-	 * \param buffer the text
-	 * \throw Error on errors
-	 */
-	IRCCD_EXPORT Value(const Buffer &buffer);
-
-	/**
-	 * Construct a value from a file.
-	 *
-	 * \param file the file
-	 * \throw Error on errors
-	 */
-	IRCCD_EXPORT Value(const File &file);
-
-	/**
 	 * Move constructor.
 	 *
 	 * \param other the value to move from
@@ -518,6 +523,7 @@
 	 * Move operator.
 	 *
 	 * \param other the value to move from
+	 * \return this
 	 */
 	inline Value &operator=(Value &&other)
 	{
@@ -529,7 +535,7 @@
 	/**
 	 * Destructor.
 	 */
-	IRCCD_EXPORT ~Value();
+	~Value();
 
 	/**
 	 * Get an iterator to the beginning.
@@ -541,7 +547,7 @@
 	{
 		assert(isArray() || isObject());
 
-		return m_type == Type::Object ? iterator(*this, m_object.begin()) : iterator(*this, m_array.begin());
+		return m_type == Type::Object ? iterator(this, m_object.begin()) : iterator(this, m_array.begin());
 	}
 
 	/**
@@ -554,7 +560,7 @@
 	{
 		assert(isArray() || isObject());
 
-		return m_type == Type::Object ? const_iterator(*this, m_object.begin()) : const_iterator(*this, m_array.begin());
+		return m_type == Type::Object ? const_iterator(this, m_object.begin()) : const_iterator(this, m_array.begin());
 	}
 
 	/**
@@ -567,7 +573,7 @@
 	{
 		assert(isArray() || isObject());
 
-		return m_type == Type::Object ? const_iterator(*this, m_object.cbegin()) : const_iterator(*this, m_array.cbegin());
+		return m_type == Type::Object ? const_iterator(this, m_object.cbegin()) : const_iterator(this, m_array.cbegin());
 	}
 
 	/**
@@ -580,7 +586,7 @@
 	{
 		assert(isArray() || isObject());
 
-		return m_type == Type::Object ? iterator(*this, m_object.end()) : iterator(*this, m_array.end());
+		return m_type == Type::Object ? iterator(this, m_object.end()) : iterator(this, m_array.end());
 	}
 
 	/**
@@ -593,7 +599,7 @@
 	{
 		assert(isArray() || isObject());
 
-		return m_type == Type::Object ? const_iterator(*this, m_object.end()) : const_iterator(*this, m_array.end());
+		return m_type == Type::Object ? const_iterator(this, m_object.end()) : const_iterator(this, m_array.end());
 	}
 
 	/**
@@ -606,7 +612,7 @@
 	{
 		assert(isArray() || isObject());
 
-		return m_type == Type::Object ? const_iterator(*this, m_object.cend()) : const_iterator(*this, m_array.cend());
+		return m_type == Type::Object ? const_iterator(this, m_object.cend()) : const_iterator(this, m_array.cend());
 	}
 
 	/**
@@ -624,21 +630,30 @@
 	 *
 	 * \return the value or false if not a boolean
 	 */
-	IRCCD_EXPORT bool toBool() const noexcept;
+	inline bool toBool() const noexcept
+	{
+		return m_type != Type::Boolean ? false : m_boolean;
+	}
 
 	/**
 	 * Get the value as integer.
 	 *
 	 * \return the value or 0 if not a integer
 	 */
-	IRCCD_EXPORT int toInt() const noexcept;
+	inline int toInt() const noexcept
+	{
+		return m_type != Type::Int ? 0 : m_integer;
+	}
 
 	/**
 	 * Get the value as real.
 	 *
 	 * \return the value or 0 if not a real
 	 */
-	IRCCD_EXPORT double toReal() const noexcept;
+	inline double toReal() const noexcept
+	{
+		return m_type != Type::Real ? 0 : m_number;
+	}
 
 	/**
 	 * Get the value as string.
@@ -646,7 +661,7 @@
 	 * \param coerce set to true to coerce the value if not a string
 	 * \return the value or empty string if not a string
 	 */
-	std::string toString(bool coerce = false) const noexcept;
+	std::string toString(bool coerce = false) const;
 
 	/**
 	 * Check if the value is boolean type.
@@ -762,7 +777,7 @@
 	}
 
 	/*
-	 * Array functions
+	 * Array functions.
 	 * ----------------------------------------------------------
 	 */
 
@@ -961,7 +976,7 @@
 	}
 
 	/*
-	 * Object functions
+	 * Object functions.
 	 * ----------------------------------------------------------
 	 */
 
@@ -1063,7 +1078,7 @@
 	{
 		assert(isObject());
 
-		return iterator(*this, m_object.find(key));
+		return iterator(this, m_object.find(key));
 	}
 
 	/**
@@ -1077,7 +1092,7 @@
 	{
 		assert(isObject());
 
-		return const_iterator(*this, m_object.find(key));
+		return const_iterator(this, m_object.find(key));
 	}
 
 	/**
@@ -1138,8 +1153,7 @@
 	/**
 	 * Return this value as JSon representation.
 	 *
-	 * \param indent, the indentation to use (0 == compact, < 0 == tabs, > 0 == number of spaces)
-	 * \param tabs, use tabs or not
+	 * \param indent the indentation to use (0 == compact, < 0 == tabs, > 0 == number of spaces)
 	 * \return the string
 	 */
 	inline std::string toJson(int indent = 2) const
@@ -1154,7 +1168,17 @@
  * \param input the input
  * \return the escaped string
  */
-IRCCD_EXPORT std::string escape(const std::string &input);
+std::string escape(const std::string &input);
+
+/**
+ * Convenient function to create an empty array.
+ *
+ * \return an empty array
+ */
+inline Value array()
+{
+	return Value(Type::Array);
+}
 
 /**
  * Convenient function for creating array from initializer list.
@@ -1168,6 +1192,16 @@
 }
 
 /**
+ * Convenient function to create an empty object.
+ *
+ * \return an empty object
+ */
+inline Value object()
+{
+	return Value(Type::Object);
+}
+
+/**
  * Convenient function for creating object from initializer list.
  *
  * \param values the values
@@ -1178,8 +1212,26 @@
 	return Value(std::map<std::string, Value>(values.begin(), values.end()));
 }
 
+/**
+ * Construct a value from a buffer.
+ *
+ * \param data the JSON data
+ * \return the parsed value
+ * \throw Error on errors
+ */
+Value fromString(const std::string &data);
+
+/**
+ * Construct a value from a file.
+ *
+ * \param path the path to the file
+ * \return the parsed value
+ * \throw Error on errors
+ */
+Value fromFile(const std::string &path);
+
 } // !json
 
 } // !irccd
 
-#endif // !IRCCD_JSON_HPP
+#endif // !JSON_HPP
--- a/lib/irccd/transport-client.cpp	Tue May 24 17:11:49 2016 +0200
+++ b/lib/irccd/transport-client.cpp	Wed May 25 20:56:48 2016 +0200
@@ -24,7 +24,7 @@
 
 void TransportClient::parse(const std::string &message)
 {
-	json::Value document(json::Buffer{message});
+	json::Value document = json::fromString(message);
 
 	if (!document.isObject())
 		throw std::invalid_argument("the message is not a valid JSON object");