ini is only three letters away from owo
This commit is contained in:
parent
b2f32e6cdf
commit
6092bb9f66
5 changed files with 250 additions and 6 deletions
|
@ -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;
|
||||||
|
}
|
|
@ -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 SectionList {
|
||||||
|
public:
|
||||||
class Section {
|
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;
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue