diff --git a/inc/config.h b/inc/config.h new file mode 100644 index 0000000..f8ba523 --- /dev/null +++ b/inc/config.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +namespace mrbesen { + +class Config { +public: + class Section { + public: + template + T get(const std::string& valueName) const; + private: + std::map values; + friend class Config; + }; + + Config(const std::string& filename); + virtual ~Config(); + + bool loadConfig(); + const Section* getSection(std::string sectionName) const; + + template + T get(const std::string& valueName) const; +private: + + std::string filename; + + std::map sections; + Section main; // config values without a section + + static const std::string SPLIT; +}; + +} diff --git a/inc/files.h b/inc/files.h index 1e8396b..4269235 100644 --- a/inc/files.h +++ b/inc/files.h @@ -24,13 +24,19 @@ enum class FileType : unsigned char { //copied from dirent.h -> see man readdir }; +//read through a file line by line call the callback with every line (without \n or \r) return true on sccess or false on error +typedef std::function fileLineCallback; +bool iterateFile(const std::string& filename, fileLineCallback clb, bool ignoreBlanks = true); +bool iterateFile(std::istream& file, fileLineCallback clb, bool ignoreBlanks = true); + +//iterate over a folder typedef std::function fileNameFilter; typedef std::function fileCallback; template bool scan(const std::string& path, std::insert_iterator it, bool prefixdir = false, fileNameFilter fnf = fileNameFilter(nullptr)); bool scan(const std::string& path, fileCallback clb, bool prefixdir = false, fileNameFilter fnf = fileNameFilter(nullptr)); -bool readFile(const std::string& name, std::ostream& output, unsigned int maxsize = 0); //reads file name and writes it line for line into output, it stops at maxsize (it might be a bit more, its not a hard limit - see it more as a hint), if maxsize is 0 it reads unlimited +bool readFile(const std::string& name, std::ostream& output, unsigned int maxsize = 0); //reads file name and writes it line for line into output, it stops at maxsize (it might be a bit more, its not a hard limit - see it more as a hint), if maxsize is 0 it reads unlimited bool readFile(int fd, std::ostream& output, unsigned int maxsize = 0); bool copy(int fd_from, int fd_to); diff --git a/inc/util.h b/inc/util.h index 49ea5be..089f8f3 100644 --- a/inc/util.h +++ b/inc/util.h @@ -24,6 +24,7 @@ bool removeStart(std::string& str, const std::string& start); bool insertEnd(std::string& str, const std::string& ending); bool insertStart(std::string& str, const std::string& start); +// splits a string into components. It removes the token from the strings. Retunrs the Count of elements in the array after the function completes. unsigned int split(const std::string& str, const std::string& token, std::string* out, unsigned int count); bool trim(std::string& str, char c = ' '); //return true if the string was changed diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..1a3c7fb --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,88 @@ +#include "config.h" + +#include "files.h" +#include "util.h" + +namespace mrbesen { + +const std::string Config::SPLIT = "."; + + +template<> +const std::string* Config::Section::get(const std::string& valueName) const { + auto it = values.find(valueName); + if(it == values.end()) return nullptr; + + return &it->second; +} + +template +T Config::Section::get(const std::string& valueName) const { + return (T) get(valueName); +} + +Config::Config(const std::string& filename) : filename(filename) {} + +Config::~Config() {} + +bool Config::loadConfig() { + main.values.clear(); + sections.clear(); + + Config::Section* currentSection = &main; + + files::iterateFile(filename, [this](const std::string& line){ + if(line[0] == '#' || line[0] == ';') return; + + if(line[0] == '[' && util::endsWith(line, "]")) { + std::string sectionName = line.substr(1, line.length()-2); + util::trim(sectionName); + if(sectionName.empty()) { + //currentSection = &main; + return; + } + //check for existing section + auto it = sections.find(sectionName); + if(it == sections.end()) { + //create new section + //TODO + sections.insert({sectionName, {}}); + } + } + + std::string split[2]; + unsigned int spl = util::split(line, "=", split, 2); + }, true); + + return false; +} + +const Config::Section* Config::getSection(std::string sectionName) const { + util::removeEnd(sectionName, SPLIT); + if(sectionName.empty()) + return &main; + auto it = sections.find(sectionName); + if(it == sections.end()) { + return nullptr; + } + return &it->second; + +} + +template +T Config::get(const std::string& valueName) const { + if(valueName.empty()) return nullptr; + + std::string out[2]; + unsigned int i = util::split(valueName, ".", out, 2); + + if(i == 1) { + //main section + return main.get(out[0]); + } + const Config::Section* sect = getSection(out[0]); + if(sect) return sect->get(out[1]); + return nullptr; +} + +} \ No newline at end of file diff --git a/src/files.cpp b/src/files.cpp index dc7cf26..d1b7c05 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -1,6 +1,7 @@ #include "files.h" #include +#include #include #include @@ -54,6 +55,28 @@ void mrbesen::files::extention(const std::string& path, std::string& ext) { ext = filestr.substr(pos+1); } +bool mrbesen::files::iterateFile(const std::string& filename, fileLineCallback clb, bool ignoreBlanks) { + std::ifstream fs(filename); + return iterateFile(fs, clb, ignoreBlanks); +} + +bool mrbesen::files::iterateFile(std::istream& file, fileLineCallback clb, bool ignoreBlanks) { + if(!clb) return false; + if(!file) return false; + + std::string line; + while(std::getline(file, line)) { + mrbesen::util::removeEnd(line, "\r"); + + if(ignoreBlanks && line.empty()) + continue; + + clb(line); + } + + return true; +} + template bool mrbesen::files::scan(const std::string& path, std::insert_iterator it, bool prefixdir, fileNameFilter fnf) { return scan(path, [&](const std::string& p, FileType t){ it = p; }, prefixdir, fnf); diff --git a/tests/filestests.cpp b/tests/filestests.cpp index 377c5aa..b5a1c1c 100644 --- a/tests/filestests.cpp +++ b/tests/filestests.cpp @@ -2,6 +2,7 @@ #include "files.h" #include "util.h" +#include #include #include #include @@ -12,6 +13,8 @@ #include #include +#define NONEXISTENT_FILE "tests/blabla.exe" + using namespace mrbesen; int testFiles_parent() { @@ -19,20 +22,20 @@ int testFiles_parent() { std::string testpath1 = "/asdf1/asdf2/test.png"; files::parent(testpath1, out); - ASSERT(out == "/asdf1/asdf2/", out); + CMPASSERT(out, "/asdf1/asdf2/"); files::parent(out, out2); - ASSERT(out2 == "/asdf1/", out2); + CMPASSERT(out2, "/asdf1/"); files::parent(out2, out); - ASSERT(out == "/", out); + CMPASSERT(out, "/"); testpath1 = "assets/tex/img1.png"; files::parent(testpath1, out); - ASSERT(out == "assets/tex/", out); + CMPASSERT(out, "assets/tex/"); files::parent("asd", out); - ASSERT(out == "", out); + CMPASSERT(out, ""); return TESTGOOD; } @@ -110,6 +113,16 @@ int testFiles_extention() { } +int testFiles_iterateFile() { + std::ifstream f1(NONEXISTENT_FILE); + ASSERT(!files::iterateFile(f1, [](const std::string&) {}), ""); + + //TODO: more tests + + return TESTGOOD; +} + + int testFiles_scan() { std::string path = "./tests"; std::list out, out2; @@ -132,7 +145,7 @@ int testFiles_readFile() { std::ostringstream buf; - ASSERT(!files::readFile("tests/blabla.exe", buf), "this file should not be read"); //try to read non existing file + ASSERT(!files::readFile(NONEXISTENT_FILE, buf), "this file should not be read"); //try to read non existing file bool r = files::readFile(std::string("tests/filestests.cpp"), buf); ASSERT(r, "failed to read"); diff --git a/tests/main.cpp b/tests/main.cpp index a114124..7683e91 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -5,6 +5,7 @@ int testFiles_parent(); int testFiles_file(); int testFiles_extention(); +int testFiles_iterateFile(); int testFiles_scan(); int testFiles_readFile(); @@ -25,7 +26,7 @@ int testUtilSplit(); int testGetDoy(); -test_t tests[] = {testFiles_parent, testFiles_file, testFiles_extention, testFiles_scan, testFiles_readFile, +test_t tests[] = {testFiles_parent, testFiles_file, testFiles_extention, testFiles_iterateFile, testFiles_scan, testFiles_readFile, testUtil_Count, testUtil_equalsIgnoreCase, testUtil_toLower, testUtil_start_endWith, testUtil_removeStart_End, testUtil_insertStart_End, testUtil_trim, testUtil_trimOnce, testUtil_base, testUtil_replace, testUtil_replace2, testStringSpliterator, testUtilSplit, testGetDoy, diff --git a/tests/test.h b/tests/test.h index 890d25b..c337abb 100644 --- a/tests/test.h +++ b/tests/test.h @@ -6,6 +6,8 @@ #define TESTDATA "./tests/data/" #define ASSERT(BED, ERR) if(!(BED)) { std::cout << __FILE__ << ":" << __LINE__ << " " << ERR << std::endl; return TESTFAILED; } +#define CMPASSERTE(A, B, ERR) if( !((A) == (B))) { std::cout << __FILE__ << ":" << __LINE__ << " is: \"" << (A) << "\" should: \"" << (B) << "\""<< std::endl; return TESTFAILED; } +#define CMPASSERT(A, B) CMPASSERTE(A, B, "") // #define ASSERT(BED) ASSERT(BED, "") typedef int (*test_t)();