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"
|
||||
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
|
||||
#define SOSC_UTIL_INI_H
|
||||
|
||||
#include <typeinfo>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "string.hpp"
|
||||
|
||||
namespace sosc {
|
||||
namespace ini {
|
||||
class File {
|
||||
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 {
|
||||
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(
|
||||
const std::string& name,
|
||||
bool required,
|
||||
bool allow_multiple,
|
||||
const std::vector<std::string>& required_fields
|
||||
const std::vector<Field>& required_fields
|
||||
) : name(name),
|
||||
required(required),
|
||||
allow_multiple(allow_multiple),
|
||||
|
@ -23,14 +76,43 @@ public:
|
|||
std::string name;
|
||||
bool required;
|
||||
bool allow_multiple;
|
||||
std::vector<std::string> required_fields;
|
||||
std::vector<Field> required_fields;
|
||||
};
|
||||
|
||||
class SectionList {
|
||||
public:
|
||||
class Section {
|
||||
|
||||
};
|
||||
public:
|
||||
const std::string& operator[] (std::string key) const;
|
||||
private:
|
||||
std::map<std::string, Section>
|
||||
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:
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
char delimiter, int count)
|
||||
{
|
||||
|
|
|
@ -30,6 +30,7 @@ std::vector<std::string> split
|
|||
std::vector<std::string> split
|
||||
(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,
|
||||
char delimiter, int count = -1);
|
||||
std::string join(const std::vector<std::string>& parts,
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
#include <ctime>
|
||||
#include <thread>
|
||||
|
||||
#include "utils/string.hpp"
|
||||
#include "db/database.hpp"
|
||||
#include "hosts/master.hpp"
|
||||
#include "hosts/slave.hpp"
|
||||
#include "utils/ini.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
template<class T, class U>
|
||||
struct _server_ctx {
|
||||
|
@ -42,6 +43,12 @@ int main(int argc, char **argv) {
|
|||
if(argc < 2)
|
||||
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(!db::init_databases(nullptr))
|
||||
return -1;
|
||||
|
|
Loading…
Reference in a new issue