Mercurial > code
diff C++/modules/Ini/ini.h @ 485:898d8b29a4f1
Switch to lowercase filenames
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 12 Nov 2015 21:53:36 +0100 |
parents | C++/modules/Ini/Ini.h@a1cec0345d76 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/modules/Ini/ini.h Thu Nov 12 21:53:36 2015 +0100 @@ -0,0 +1,546 @@ +/* + * ini.h -- .ini file parsing + * + * Copyright (c) 2013-2015 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 <cassert> +#include <exception> +#include <stdexcept> +#include <string> +#include <vector> + +/** + * Namespace for ini related classes. + */ +namespace ini { + +class Document; + +/** + * @class Error + * @brief Error in a file + */ +class Error : public std::exception { +private: + int m_line; //!< line number + int m_column; //!< line column + std::string m_message; //!< error message + +public: + /** + * Constructor. + * + * @param l the line + * @param c the column + * @param m the message + */ + inline Error(int l, int c, std::string m) noexcept + : m_line{l} + , m_column{c} + , m_message{std::move(m)} + { + } + + /** + * Get the line number. + * + * @return the line + */ + inline int line() const noexcept + { + return m_line; + } + + /** + * Get the column number. + * + * @return the column + */ + inline int column() const noexcept + { + return m_column; + } + + /** + * Return the raw error message (no line and column shown). + * + * @return the error message + */ + const char *what() const noexcept override + { + return m_message.c_str(); + } +}; + +/** + * @class Token + * @brief Describe a token read in the .ini source + * + * This class can be used when you want to parse a .ini file yourself. + * + * @see Document::analyze + */ +class Token { +public: + /** + * @brief Token type + */ + enum Type { + Include, //!< include statement + Section, //!< [section] + Word, //!< word without quotes + QuotedWord, //!< word with quotes + Assign, //!< = assignment + ListBegin, //!< begin of list ( + ListEnd, //!< end of list ) + Comma //!< list separation + }; + +private: + Type m_type; + int m_line; + int m_column; + std::string m_value; + +public: + /** + * Construct a token. + * + * @param type the type + * @param line the line + * @param column the column + * @param value the value + */ + Token(Type type, int line, int column, std::string value = "") noexcept + : m_type{type} + , m_line{line} + , m_column{column} + { + switch (type) { + case Include: + m_value = "@include"; + break; + case Section: + case Word: + case QuotedWord: + m_value = value; + break; + case Assign: + m_value = "="; + break; + case ListBegin: + m_value = "("; + break; + case ListEnd: + m_value = ")"; + break; + case Comma: + m_value = ","; + break; + default: + break; + } + } + + /** + * Get the type. + * + * @return the type + */ + inline Type type() const noexcept + { + return m_type; + } + + /** + * Get the line. + * + * @return the line + */ + inline int line() const noexcept + { + return m_line; + } + + /** + * Get the column. + * + * @return the column + */ + inline int column() const noexcept + { + return m_column; + } + + /** + * Get the value. For words, quoted words and section, the value is the content. Otherwise it's the + * characters parsed. + * + * @return the value + */ + inline const std::string &value() const noexcept + { + return m_value; + } +}; + +/** + * List of tokens in order they are analyzed. + */ +using Tokens = std::vector<Token>; + +/** + * @class Option + * @brief Option definition. + */ +class Option : public std::vector<std::string> { +private: + std::string m_key; + +public: + /** + * Construct an empty option. + * + * @param key the key + * @param value the value + */ + inline Option(std::string key) noexcept + : std::vector<std::string>{} + , m_key{std::move(key)} + { + } + + /** + * Construct a single option. + * + * @param key the key + * @param value the value + */ + inline Option(std::string key, std::string value) noexcept + : m_key{std::move(key)} + { + push_back(std::move(value)); + } + + /** + * Construct a list option. + * + * @param key the key + * @param values the values + */ + inline Option(std::string key, std::vector<std::string> values) noexcept + : std::vector<std::string>{std::move(values)} + , m_key{std::move(key)} + { + } + + /** + * 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 + { + static std::string dummy; + + return empty() ? dummy : (*this)[0]; + } +}; + +/** + * @class Section + * @brief Section that contains one or more options. + */ +class Section : public std::vector<Option> { +private: + std::string m_key; + +public: + /** + * Construct a section with its name. + * + * @param key the key + * @pre key must not be empty + */ + inline Section(std::string key) noexcept + : m_key{std::move(key)} + { + assert(!m_key.empty()); + } + + /** + * Get the section key. + * + * @return the key + */ + inline const std::string &key() const noexcept + { + return m_key; + } + + /** + * Check if the section contains a specific option. + * + * @param key the option key + * @return true if the option exists + */ + inline bool contains(const std::string &key) const noexcept + { + return find(key) != end(); + } + + /** + * Access an option at the specified key. + * + * @param key the key + * @return the option + * @pre contains(key) must return true + */ + inline Option &operator[](const std::string &key) + { + assert(contains(key)); + + return *find(key); + } + + /** + * Overloaded function. + * + * @param key the key + * @return the option + * @pre contains(key) must return true + */ + inline const Option &operator[](const std::string &key) const + { + assert(contains(key)); + + return *find(key); + } + + /** + * Find an option by key and return an iterator. + * + * @param key the key + * @return the iterator or end() if not found + */ + inline iterator find(const std::string &key) noexcept + { + return std::find_if(begin(), end(), [&] (const auto &o) { + return o.key() == key; + }); + } + + /** + * Find an option by key and return an iterator. + * + * @param key the key + * @return the iterator or end() if not found + */ + inline const_iterator find(const std::string &key) const noexcept + { + return std::find_if(cbegin(), cend(), [&] (const auto &o) { + return o.key() == key; + }); + } + + /** + * Inherited operators. + */ + using std::vector<Option>::operator[]; +}; + +/** + * @class File + * @brief Source for reading .ini files. + */ +class File { +public: + /** + * Path to the file. + */ + std::string path; +}; + +/** + * @class Buffer + * @brief Source for reading ini from text. + * @note the include statement is not supported with buffers. + */ +class Buffer { +public: + /** + * The ini content. + */ + std::string text; +}; + +/** + * @class Document + * @brief Ini config file loader + */ +class Document : public std::vector<Section> { +private: + std::string m_path; + +public: + /** + * Analyze a file and extract tokens. If the function succeeds, that does not mean the content is valid, + * it just means that there are no syntax error. + * + * For example, this class does not allow adding options under no sections and this function will not + * detect that issue. + * + * @param file the file to read + * @return the list of tokens + * @throws Error on errors + */ + static Tokens analyze(const File &file); + + /** + * Overloaded function for buffers. + * + * @param buffer the buffer to read + * @return the list of tokens + * @throws Error on errors + */ + static Tokens analyze(const Buffer &buffer); + + /** + * Show all tokens and their description. + * + * @param tokens the tokens + */ + static void dump(const Tokens &tokens); + + /** + * Construct a document from a file. + * + * @param file the file to read + * @throws Error on errors + */ + Document(const File &file); + + /** + * Overloaded constructor for buffers. + * + * @param buffer the buffer to read + * @throws Error on errors + */ + Document(const Buffer &buffer); + + /** + * Get the current document path, only useful when constructed from File source. + * + * @return the path + */ + inline const std::string &path() const noexcept + { + return m_path; + } + + /** + * Check if a document has a specific section. + * + * @param key the key + * @return true if the document contains the section + */ + inline bool contains(const std::string &key) const noexcept + { + return std::find_if(begin(), end(), [&] (const auto &sc) { return sc.key() == key; }) != end(); + } + + /** + * Access a section at the specified key. + * + * @param key the key + * @return the section + * @pre contains(key) must return true + */ + inline Section &operator[](const std::string &key) + { + assert(contains(key)); + + return *find(key); + } + + /** + * Overloaded function. + * + * @param key the key + * @return the section + * @pre contains(key) must return true + */ + inline const Section &operator[](const std::string &key) const + { + assert(contains(key)); + + return *find(key); + } + + /** + * Find a section by key and return an iterator. + * + * @param key the key + * @return the iterator or end() if not found + */ + inline iterator find(const std::string &key) noexcept + { + return std::find_if(begin(), end(), [&] (const auto &o) { + return o.key() == key; + }); + } + + /** + * Find a section by key and return an iterator. + * + * @param key the key + * @return the iterator or end() if not found + */ + inline const_iterator find(const std::string &key) const noexcept + { + return std::find_if(cbegin(), cend(), [&] (const auto &o) { + return o.key() == key; + }); + } + + /** + * Inherited operators. + */ + using std::vector<Section>::operator[]; +}; + +} // !ini + +#endif // !_INI_H_