view C++/Ini.h @ 327:78e8f9a3b233

Ini: - Add support for @include - Add more tests - Added lot of documentation TODO: - Add lots of test with errored files
author David Demelier <markand@malikania.fr>
date Tue, 03 Mar 2015 21:21:11 +0100
parents d52a69f9f029
children
line wrap: on
line source

/*
 * Ini.h -- .ini file parsing
 *
 * Copyright (c) 2013, 2014 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
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _INI_H_
#define _INI_H_

/**
 * @file Ini.h
 * @brief Configuration file parser
 */

#include <algorithm>
#include <deque>
#include <stdexcept>
#include <string>

/**
 * @class IniError
 * @brief Error in a file
 */
class IniError : public std::exception {
private:
	int m_line;
	int m_position;
	std::string m_error;

public:
	/**
	 * Construct an error.
	 *
	 * @param line the line
	 * @param position the position
	 * @param error the error
	 */
	inline IniError(int line, int position, std::string error)
		: m_line(line)
		, m_position(position)
		, m_error(std::move(error))
	{
	}

	/**
	 * Return the line number.
	 *
	 * @return the line
	 */
	inline int line() const noexcept
	{
		return m_line;
	}

	/**
	 * Return the position in the current line.
	 *
	 * @return the position
	 */
	inline int position() const noexcept
	{
		return m_position;
	}

	/**
	 * Get the error string.
	 *
	 * @return the string
	 */
	inline const char *what() const noexcept
	{
		return m_error.c_str();
	}
};

/**
 * @class IniOption
 * @brief Option definition
 */
class IniOption {
private:
	std::string m_key;
	std::string m_value;

public:
	/**
	 * Construct an option.
	 *
	 * @param key the key
	 * @param value the value
	 */
	inline IniOption(std::string key, std::string value)
		: m_key(std::move(key))
		, m_value(std::move(value))
	{
	}

	/**
	 * Get the option key.
	 *
	 * @return the key
	 */
	inline const std::string &key() const noexcept
	{
		return m_key;
	}

	/**
	 * Get the option value.
	 *
	 * @return the value
	 */
	inline const std::string &value() const noexcept
	{
		return m_value;
	}
};

/**
 * @class IniSection
 * @brief Section that contains one or more options
 */
class IniSection {
private:
	std::string m_key;
	std::deque<IniOption> m_options;

	template <typename T>
	T find(const std::string &key) const
	{
		auto it = std::find_if(m_options.begin(), m_options.end(), [&] (const IniOption &o) {
			return o.key() == key;
		});

		if (it == m_options.end())
			throw std::out_of_range("option " + key + " not found");

		return const_cast<T>(*it);
	}

public:
	/**
	 * Default constructor has no sections and no values.
	 */
	IniSection() = default;

	/**
	 * Construct a section with a set of options.
	 *
	 * @param key the section name
	 * @param options the list of options
	 */
	inline IniSection(std::string key, std::deque<IniOption> options = {}) noexcept
		: m_key(std::move(key))
		, m_options(std::move(options))
	{
	}

	/**
	 * Get the section key.
	 *
	 * @return the key
	 */
	inline const std::string &key() const noexcept
	{
		return m_key;
	}

	/**
	 * Get an iterator to the beginning.
	 *
	 * @return the iterator
	 */
	inline auto begin() noexcept
	{
		return m_options.begin();
	}

	/**
	 * Overloaded function.
	 *
	 * @return the iterator
	 */
	inline auto begin() const noexcept
	{
		return m_options.begin();
	}

	/**
	 * Overloaded function.
	 *
	 * @return the iterator
	 */
	inline auto cbegin() const noexcept
	{
		return m_options.cbegin();
	}

	/**
	 * Get an iterator to the end.
	 *
	 * @return the iterator
	 */
	inline auto end() noexcept
	{
		return m_options.end();
	}

	/**
	 * Overloaded function.
	 *
	 * @return the iterator
	 */
	inline auto end() const noexcept
	{
		return m_options.end();
	}

	/**
	 * Overloaded function.
	 *
	 * @return the iterator
	 */
	inline auto cend() const noexcept
	{
		return m_options.cend();
	}

	/**
	 * Append an option.
	 *
	 * @param option the option to add
	 */
	inline void push_back(IniOption option)
	{
		m_options.push_back(std::move(option));
	}

	/**
	 * Push an option to the beginning.
	 *
	 * @param option the option to add
	 */
	inline void push_front(IniOption option)
	{
		m_options.push_front(std::move(option));
	}

	/**
	 * Get the number of options in that section.
	 *
	 * @return the size
	 */
	inline unsigned size() const noexcept
	{
		return m_options.size();
	}

	/**
	 * Access an option at the specified index.
	 *
	 * @param index the index
	 * @return the option
	 * @warning No bounds checking is performed
	 */
	inline IniOption &operator[](int index) noexcept
	{
		return m_options[index];
	}

	/**
	 * Access an option at the specified index.
	 *
	 * @param index the index
	 * @return the option
	 * @warning No bounds checking is performed
	 */
	inline const IniOption &operator[](int index) const noexcept
	{
		return m_options[index];
	}

	/**
	 * Access an option at the specified key.
	 *
	 * @param key the key
	 * @return the option
	 * @warning No bounds checking is performed
	 */
	inline IniOption &operator[](const std::string &key)
	{
		return find<IniOption &>(key);
	}

	/**
	 * Access an option at the specified key.
	 *
	 * @param key the key
	 * @return the option
	 * @warning No bounds checking is performed
	 */
	inline const IniOption &operator[](const std::string &key) const
	{
		return find<const IniOption &>(key);
	}
};

/**
 * @class Ini
 * @brief Ini config file loader
 */
class Ini {
private:
	std::deque<IniSection> m_sections;

	template <typename T>
	T find(const std::string &key) const
	{
		auto it = std::find_if(m_sections.begin(), m_sections.end(), [&] (const IniSection &s) {
			return s.key() == key;
		});

		if (it == m_sections.end())
			throw std::out_of_range("section " + key + " not found");

		return const_cast<T>(*it);
	}

public:
	/**
	 * Default constructor with an empty configuration.
	 */
	Ini() = default;

	/**
	 * Open the path as the configuration file.
	 *
	 * @param path the path
	 * @throw IniError on any error
	 */
	Ini(const std::string &path);

	/**
	 * Get an iterator to the beginning.
	 *
	 * @return the iterator
	 */
	inline auto begin() noexcept
	{
		return m_sections.begin();
	}

	/**
	 * Overloaded function.
	 *
	 * @return the iterator
	 */
	inline auto begin() const noexcept
	{
		return m_sections.begin();
	}

	/**
	 * Overloaded function.
	 *
	 * @return the iterator
	 */
	inline auto cbegin() const noexcept
	{
		return m_sections.cbegin();
	}

	/**
	 * Get an iterator to the end.
	 *
	 * @return the iterator
	 */
	inline auto end() noexcept
	{
		return m_sections.end();
	}

	/**
	 * Overloaded function.
	 *
	 * @return the iterator
	 */
	inline auto end() const noexcept
	{
		return m_sections.end();
	}

	/**
	 * Overloaded function.
	 *
	 * @return the iterator
	 */
	inline auto cend() const noexcept
	{
		return m_sections.cend();
	}

	/**
	 * Get the number of sections in the configuration.
	 *
	 * @return the size
	 */
	inline unsigned size() const noexcept
	{
		return m_sections.size();
	}

	/**
	 * Append a section to the end.
	 *
	 * @param section the section to add
	 */
	inline void push_back(IniSection section)
	{
		m_sections.push_back(std::move(section));
	}

	/**
	 * Add a section to the beginning.
	 *
	 * @param section the section to add
	 */
	inline void push_front(IniSection section)
	{
		m_sections.push_front(std::move(section));
	}

	/**
	 * Access a section at the specified index.
	 *
	 * @param index the index
	 * @return the section
	 * @warning No bounds checking is performed
	 */
	inline IniSection &operator[](int index) noexcept
	{
		return m_sections[index];
	}

	/**
	 * Access a section at the specified index.
	 *
	 * @param index the index
	 * @return the section
	 * @warning No bounds checking is performed
	 */
	inline const IniSection &operator[](int index) const noexcept
	{
		return m_sections[index];
	}

	/**
	 * Access a section at the specified key.
	 *
	 * @param key the key
	 * @return the section
	 * @warning No bounds checking is performed
	 */
	inline IniSection &operator[](const std::string &key)
	{
		return find<IniSection &>(key);
	}

	/**
	 * Access a section at the specified key.
	 *
	 * @param key the key
	 * @return the section
	 * @warning No bounds checking is performed
	 */
	inline const IniSection &operator[](const std::string &key) const
	{
		return find<IniSection &>(key);
	}
};

#endif // !_INI_H_