ini is only three letters away from owo

This commit is contained in:
malloc 2018-11-07 16:28:33 -06:00
parent b2f32e6cdf
commit 6092bb9f66
5 changed files with 250 additions and 6 deletions

View file

@ -1,2 +1,150 @@
#include "ini.hpp" #include "ini.hpp"
using namespace sosc::ini;
File* File::Open
(const std::string& filename, const std::vector<Rule>& rules)
{
auto ini = new File;
ini->filename = filename;
std::ifstream file;
file.open(filename);
if(!file.is_open())
throw LoadError(ini, -1,
"Could not open file."
);
int line_number = 0;
std::string line, tmp;
File::SectionList::Section* active_section = nullptr;
while(std::getline(file, line)) {
++line_number;
str::trim(&line);
if(line == "")
continue;
if(line[0] == ';')
continue;
if(line[0] == '[') {
str::tolower(&line);
if(line[line.length() - 1] != ']' || line.length() == 2)
throw LoadError(ini, line_number,
"Section header must begin with [ and end with ]."
);
tmp = line.substr(1, line.length() - 2);
if(ini->section_lists.count(tmp) == 0)
ini->section_lists[tmp] = SectionList();
auto sections = &(ini->section_lists[tmp].sections);
sections->push_back(SectionList::Section());
active_section = &((*sections)[sections->size() - 1]);
} else {
if(active_section == nullptr)
throw LoadError(ini, line_number,
"Key-value pair field must be after section header."
);
auto parts = str::split(line, '=', 2);
if(parts.size() < 2)
throw LoadError(ini, line_number,
"Key-value pair field must be separated by '='."
);
str::tolower(str::trim(&(parts[0])));
str::trim(&(parts[1]));
if(parts[0] == "" || active_section->values.count(parts[0]) == 0)
throw LoadError(ini, line_number,
"Duplicate key-value pair field in section."
);
active_section->values[parts[0]] = parts[1];
}
}
for(auto& rule : rules) {
if(rule.required && ini->section_lists.count(rule.name) == 0)
throw LoadError(ini, -1, str::join({
"Required section '", rule.name, "' not found."
}));
if(!rule.allow_multiple && ini[rule.name].sections.size() > 1)
throw LoadError(ini, -1, str::join({
"Multiple instances of '", rule.name, "' section "
"when explicitely not allowed."
}));
for(auto& section : ini[rule.name].sections) {
for(auto &field : rule.required_fields) {
str::tolower(&field.name);
if(section.values.count(field.name) == 0)
throw LoadError(ini, -1, str::join({
"Required field '", field.name, "' in section '",
rule.name, "' not found."
}));
if(!field.Test())
throw LoadError(ini, -1, str::join({
"Field '", field.name, "' in section '",rule.name, "' "
"cannot be casted to requested type."
}));
}
}
}
return ini;
}
std::runtime_error File::LoadError
(File* file, int line, const std::string &error)
{
delete file;
if(line > 0)
return std::runtime_error(str::join(
{"LOAD ERROR IN '", file->filename, "' L", TOSTR(line), ": ", error}
));
else
return std::runtime_error(str::join(
{"LOAD ERROR IN '", file->filename, "': ", error}
));
}
const File::SectionList&
File::operator[] (std::string name) const
{
str::tolower(&name);
if(this->section_lists.count(name) > 0)
return this->section_lists[name];
else
throw std::range_error("Section name not found.");
}
const std::string&
File::SectionList::operator[] (std::string key) const
{
return this->sections[0][key];
}
const File::SectionList::Section&
File::SectionList::operator[] (int index) const
{
if(index < sections.size())
return this->sections[index];
else
throw std::range_error("Section index out-of-bounds.");
}
const std::string&
File::SectionList::Section::operator[] (std::string key) const
{
str::tolower(&key);
if(this->values.count(key) > 0)
return this->values[key];
else
throw std::range_error("Key not found in section.");
}
void File::Close() {
delete this;
}

View file

@ -1,20 +1,73 @@
#ifndef SOSC_UTIL_INI_H #ifndef SOSC_UTIL_INI_H
#define SOSC_UTIL_INI_H #define SOSC_UTIL_INI_H
#include <typeinfo>
#include <fstream>
#include <sstream>
#include <vector> #include <vector>
#include <map> #include <map>
#include "string.hpp"
namespace sosc { namespace sosc {
namespace ini { namespace ini {
class File { class File {
public: public:
struct Proxy {
std::string value;
Proxy(const std::string& value) : value(value) {};
explicit operator bool() const {
if(this->value == "1" || this->value == "true")
return true;
else if(this->value == "0" || this->value == "false")
return false;
else
throw std::bad_cast();
}
template<
typename T,
typename Decayed = typename std::decay<T>::type,
typename = typename std::enable_if<
!std::is_same<const char*, Decayed>::value
&& !std::is_same<std::allocator<char>, Decayed>::value
&& !std::is_same<std::initializer_list<char>, Decayed>::value
>::type
> explicit operator T() {
std::stringstream ss;
ss << this->value;
T retval;
if(!(ss >> retval))
throw std::bad_cast();
else
return retval;
}
};
struct Rule { struct Rule {
template<typename T = std::string>
struct Field {
Field(const std::string& name) : name(name) {}
bool Test() {
try {
(T)Proxy(this->name);
return true;
} catch(...) {
return false;
}
}
std::string name;
};
Rule() = delete; Rule() = delete;
Rule( Rule(
const std::string& name, const std::string& name,
bool required, bool required,
bool allow_multiple, bool allow_multiple,
const std::vector<std::string>& required_fields const std::vector<Field>& required_fields
) : name(name), ) : name(name),
required(required), required(required),
allow_multiple(allow_multiple), allow_multiple(allow_multiple),
@ -23,14 +76,43 @@ public:
std::string name; std::string name;
bool required; bool required;
bool allow_multiple; bool allow_multiple;
std::vector<std::string> required_fields; std::vector<Field> required_fields;
}; };
class Section { class SectionList {
public:
class Section {
public:
const std::string& operator[] (std::string key) const;
private:
Section() = default;
std::map<std::string, std::string> values;
friend class File;
};
const std::string& operator[] (std::string key) const;
const Section& operator[] (int index) const;
private:
SectionList() = default;
std::vector<Section> sections;
friend class File;
}; };
File(const File&) = delete;
static File* Open
(const std::string& filename, const std::vector<Rule>& rules = {});
void Close();
const SectionList& operator[] (std::string name) const;
private: private:
std::map<std::string, Section> File() = default;
static std::runtime_error LoadError
(File* file, int line, const std::string& error);
std::string filename;
std::map<std::string, SectionList> section_lists;
}; };
}} }}

View file

@ -73,6 +73,12 @@ std::vector<std::string> sosc::str::split
return parts; return parts;
} }
std::string sosc::str::join
(const std::vector<std::string>& parts, int count)
{
return join(parts, "", count);
}
std::string sosc::str::join(const std::vector<std::string>& parts, std::string sosc::str::join(const std::vector<std::string>& parts,
char delimiter, int count) char delimiter, int count)
{ {

View file

@ -30,6 +30,7 @@ std::vector<std::string> split
std::vector<std::string> split std::vector<std::string> split
(const std::string& str, std::string delimiter, int count = -1); (const std::string& str, std::string delimiter, int count = -1);
std::string join(const std::vector<std::string>& parts, int count = -1);
std::string join(const std::vector<std::string>& parts, std::string join(const std::vector<std::string>& parts,
char delimiter, int count = -1); char delimiter, int count = -1);
std::string join(const std::vector<std::string>& parts, std::string join(const std::vector<std::string>& parts,

View file

@ -3,10 +3,11 @@
#include <ctime> #include <ctime>
#include <thread> #include <thread>
#include "utils/string.hpp"
#include "db/database.hpp" #include "db/database.hpp"
#include "hosts/master.hpp" #include "hosts/master.hpp"
#include "hosts/slave.hpp" #include "hosts/slave.hpp"
#include "utils/ini.hpp"
#include "utils/string.hpp"
template<class T, class U> template<class T, class U>
struct _server_ctx { struct _server_ctx {
@ -42,6 +43,12 @@ int main(int argc, char **argv) {
if(argc < 2) if(argc < 2)
return -1; return -1;
auto test = ini::File::Open("test.ini", {
ini::File::Rule("test section", true, false, {
ini::File::Rule::Field<bool>("test")
}),
});
if(argv[1][0] == 'm') { if(argv[1][0] == 'm') {
if(!db::init_databases(nullptr)) if(!db::init_databases(nullptr))
return -1; return -1;