diff --git a/include/filestorm/filetree.h b/include/filestorm/filetree.h index b8cedca..ff79a00 100644 --- a/include/filestorm/filetree.h +++ b/include/filestorm/filetree.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -13,21 +15,37 @@ class FileTree { static std::atomic directory_count; static std::atomic file_count; + static std::atomic directory_id; + static std::atomic file_id; + + unsigned int _max_depth; + struct Node { std::string name; Type type; Node* parent; long size; - std::map> children; + std::map> folders; + std::map> files; Node(const std::string& n, Type t, Node* p, long size = 0) : name(n), type(t), parent(p), size(size) {} + std::string path(bool include_root = false) const { + if (parent == nullptr) { + if (include_root) { + return name; + } else { + return ""; + } + } + return parent->path(include_root) + "/" + name; + } }; private: std::unique_ptr root; public: - FileTree(const std::string& rootName); + FileTree(const std::string& rootName, unsigned int max_depth = 0); Node* addDirectory(Node* parent, const std::string& dirName); void remove(Node* node); FileTree::Node* addFile(Node* parent, const std::string& fileName, long size = 0); @@ -45,6 +63,9 @@ class FileTree { int getDirectoryCount() const { return directory_count; } int getFileCount() const { return file_count; } + std::string newDirectoryPath(); + std::string newFilePath(); + private: void printRec(const Node* node, int depth) const; }; diff --git a/include/filestorm/scenarios/scenario.h b/include/filestorm/scenarios/scenario.h index 111a7d2..2532009 100644 --- a/include/filestorm/scenarios/scenario.h +++ b/include/filestorm/scenarios/scenario.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -73,6 +74,23 @@ class BasicScenario : public Scenario { class AgingScenario : public Scenario { protected: + enum States { + S, + CREATE, + ALTER, + DELETE, + CREATE_FILE, + CREATE_DIR, + ALTER_SMALLER, + ALTER_BIGGER, + ALTER_METADATA, + DELETE_FILE, + DELETE_DIR, + END, + }; + + void compute_probabilities(std::map& probabilities, FileTree& tree); + public: AgingScenario(); ~AgingScenario(); diff --git a/include/filestorm/utils.h b/include/filestorm/utils.h index 4aec626..f8dbeab 100644 --- a/include/filestorm/utils.h +++ b/include/filestorm/utils.h @@ -4,3 +4,5 @@ #include std::vector split(const std::string& str, char delimiter); +std::string strip(const std::string& str); +std::string strip(const std::string& str, const char stripChar = ' '); \ No newline at end of file diff --git a/include/filestorm/utils/psm.h b/include/filestorm/utils/psm.h index c901517..0cc3610 100644 --- a/include/filestorm/utils/psm.h +++ b/include/filestorm/utils/psm.h @@ -1,62 +1,54 @@ #pragma once +#include + #include #include #include -class State { -public: - State(std::string name) : name(name) {} - State(const char* name) : name(name) {} - const std::string& getName() const { return name; } - -private: - std::string name; -}; - class Transition { private: - State& _from; - State& _to; + int _from; + int _to; - std::function _probability_function; + std::string _probability_key; public: - Transition(State& from, State& to, std::function probability_callback) : _from(from), _to(to), _probability_function(probability_callback) {} + Transition(int from, int to, std::string probability_key) : _from(from), _to(to), _probability_key(probability_key) {} - const State& from() const { return _from; } - const State& to() const { return _to; } - double probability() const { return _probability_function(); } + int from() const { return _from; } + int to() const { return _to; } + std::string probability_key() const { return _probability_key; } }; class ProbabilisticStateMachine { public: - ProbabilisticStateMachine(std::map& transitions, State& init) : _transitions(transitions), _currentState(init) {} + ProbabilisticStateMachine(std::map& transitions, int init) : _transitions(transitions), _currentState(init) {} - void performTransition() { + void performTransition(std::map& probabilities) { // Choose a transition based on probabilities - double randomValue = static_cast(rand()) / RAND_MAX; + double randomValue = ((double)rand() / (RAND_MAX)); double cumulativeProbability = 0.0; for (const auto& transition : _transitions) { - if (transition.second.from().getName() != _currentState.getName()) { + if (transition.second.from() != _currentState) { continue; } - - cumulativeProbability += transition.second.probability(); + cumulativeProbability += probabilities[transition.second.probability_key()]; + // spdlog::debug("Cumulative probability: {}", cumulativeProbability); if (randomValue <= cumulativeProbability) { // Transition to the next state _currentState = transition.second.to(); - std::cout << "Performed transition from " << transition.second.from().getName() << " to " << transition.second.to().getName() << std::endl; - break; + return; } } + throw std::runtime_error("No transitions from current state"); } - const State& getCurrentState() const { return _currentState; } + int getCurrentState() const { return _currentState; } private: std::map& _transitions; - State& _currentState; + int _currentState; }; diff --git a/misc/probs.cpp b/misc/probs.cpp new file mode 100644 index 0000000..715996b --- /dev/null +++ b/misc/probs.cpp @@ -0,0 +1,13 @@ +#include + +double CAF(double x) { return sqrt(1 - (x * x)); } + +int main() { + double capacity = 1024; + double free = 1024; + while (free > 0) { + std::cout << "capacity: " << capacity << " free: " << free << " pC= " << CAF((float(capacity - free) / float(capacity))) << std::endl; + free -= 1; + } + return 0; +} \ No newline at end of file diff --git a/source/filetree.cpp b/source/filetree.cpp index 82e7848..8b7a91a 100644 --- a/source/filetree.cpp +++ b/source/filetree.cpp @@ -1,33 +1,37 @@ #include #include +#include // Include the necessary header file std::atomic FileTree::directory_count(0); std::atomic FileTree::file_count(0); -FileTree::FileTree(const std::string& rootName) { root = std::make_unique(rootName, Type::DIRECTORY, nullptr); } +std::atomic FileTree::directory_id(0); +std::atomic FileTree::file_id(0); + +FileTree::FileTree(const std::string& rootName, unsigned int max_depth) : _max_depth(max_depth) { root = std::make_unique(rootName, Type::DIRECTORY, nullptr); } FileTree::Node* FileTree::addDirectory(Node* parent, const std::string& dirName) { if (parent->type == Type::FILE) { throw std::runtime_error("Can't add directory to a file node!"); } - if (parent->children.find(dirName) != parent->children.end()) { + if (parent->folders.find(dirName) != parent->folders.end()) { throw std::runtime_error("Directory already exists!"); } - parent->children[dirName] = std::make_unique(dirName, Type::DIRECTORY, parent); + parent->folders[dirName] = std::make_unique(dirName, Type::DIRECTORY, parent); directory_count++; - return parent->children[dirName].get(); + return parent->folders[dirName].get(); } FileTree::Node* FileTree::addFile(Node* parent, const std::string& fileName, long size) { if (parent->type == Type::FILE) { throw std::runtime_error("Can't add file to a file node!"); } - if (parent->children.find(fileName) != parent->children.end()) { - throw std::runtime_error("File already exists!"); + if (parent->files.find(fileName) != parent->files.end()) { + throw std::runtime_error("File already registered!"); } - parent->children[fileName] = std::make_unique(fileName, Type::FILE, parent, size); + parent->files[fileName] = std::make_unique(fileName, Type::FILE, parent, size); file_count++; - return parent->children[fileName].get(); + return parent->files[fileName].get(); } void FileTree::print() const { printRec(root.get(), 0); } @@ -40,32 +44,38 @@ void FileTree::printRec(const Node* node, int depth) const { } std::cout << std::endl; - for (const auto& child : node->children) { + for (const auto& child : node->folders) { printRec(child.second.get(), depth + 1); } + for (const auto& child : node->files) { + for (int i = 0; i < depth + 1; ++i) std::cout << "--"; + std::cout << child.second->name << std::endl; + } } void FileTree::remove(Node* node) { + if (node->parent == nullptr) { + throw std::runtime_error("Can't remove root node!"); + } // recursively remove all children and optionaly parent - while (node->children.size() > 0) { - remove(node->children.begin()->second.get()); + while (node->folders.size() > 0 and node->files.size() > 0) { + remove(node->folders.begin()->second.get()); + remove(node->files.begin()->second.get()); } if (node->type == Type::DIRECTORY) { + node->parent->folders.erase(node->name); directory_count--; } else { + node->parent->files.erase(node->name); file_count--; } - - if (node->parent == nullptr) { - throw std::runtime_error("Can't remove root node!"); - } - node->parent->children.erase(node->name); } FileTree::Node* FileTree::getRoot() const { return root.get(); } FileTree::Node* FileTree::mkdir(std::string path, bool recursively) { // split path by / + path = strip(path, '/'); auto pathParts = split(path, '/'); if (pathParts.size() == 0) { throw std::runtime_error("Path is empty!"); @@ -75,7 +85,7 @@ FileTree::Node* FileTree::mkdir(std::string path, bool recursively) { if (part == "") { throw std::runtime_error("Path contains empty part!"); } - if (current->children.find(part) == current->children.end()) { + if (current->folders.find(part) == current->folders.end() and current->files.find(part) == current->files.end()) { if (recursively or part == pathParts.back()) { current = addDirectory(current, part); } else { @@ -85,7 +95,7 @@ FileTree::Node* FileTree::mkdir(std::string path, bool recursively) { if (part == pathParts.back()) { throw std::runtime_error("Directory already registered!"); } - current = current->children[part].get(); + current = current->folders[part].get(); } } return current; @@ -93,6 +103,7 @@ FileTree::Node* FileTree::mkdir(std::string path, bool recursively) { FileTree::Node* FileTree::mkfile(std::string path) { // split path by / + path = strip(path, '/'); auto pathParts = split(path, '/'); if (pathParts.size() == 0) { throw std::runtime_error("Path is empty!"); @@ -102,17 +113,15 @@ FileTree::Node* FileTree::mkfile(std::string path) { if (part == "") { throw std::runtime_error("Path contains empty part!"); } - if (current->children.find(part) == current->children.end()) { + if (current->folders.find(part) == current->folders.end()) { if (part == pathParts.back()) { current = addFile(current, part); return current; } else { throw std::runtime_error("Directory isn't registered!"); } - } else if (current->children[part]->type == Type::FILE) { - throw std::runtime_error("File already registered!"); } else { - current = current->children[part].get(); + current = current->folders[part].get(); } } throw std::runtime_error("mkfile error: File probably already registered!"); @@ -120,6 +129,7 @@ FileTree::Node* FileTree::mkfile(std::string path) { FileTree::Node* FileTree::getNode(std::string path) { // split path by / + path = strip(path, '/'); if (path == "/") { return root.get(); } @@ -132,10 +142,17 @@ FileTree::Node* FileTree::getNode(std::string path) { if (part == "") { throw std::runtime_error("Path contains empty part!"); } - if (current->children.find(part) == current->children.end()) { - throw std::runtime_error("Node doesn't exist!"); + if (current->files.find(part) != current->files.end()) { + if (part == pathParts.back()) { + return current->files[part].get(); + } else { + throw std::runtime_error("Node doesn't exist!"); + } + } + if (current->folders.find(part) != current->folders.end()) { + current = current->folders[part].get(); } else { - current = current->children[part].get(); + throw std::runtime_error("Node doesn't exist!"); } } return current; @@ -143,6 +160,7 @@ FileTree::Node* FileTree::getNode(std::string path) { void FileTree::rm(std::string path, bool recursively) { // split path by / + path = strip(path, '/'); auto pathParts = split(path, '/'); if (pathParts.size() == 0) { throw std::runtime_error("Path is empty!"); @@ -162,7 +180,7 @@ void FileTree::rm(std::string path, bool recursively) { remove(current); return; } else { - if (current->children.size() == 0) { + if (current->files.size() == 0 and current->folders.size() == 0) { remove(current); return; } else { @@ -170,4 +188,48 @@ void FileTree::rm(std::string path, bool recursively) { } } } +} + +std::string FileTree::newDirectoryPath() { + std::string new_dir_name = fmt::format("dir_{}", directory_id++); + unsigned int depth = 0; + unsigned int rand_selected_depth = rand() % _max_depth; + + auto current_root = root.get(); + + while (depth < rand_selected_depth) { + auto current_dir_count = current_root->folders.size(); + if (current_dir_count == 0) { + break; + } + auto selected_dir_index = rand() % current_dir_count; + auto selected_dir = current_root->folders.begin(); + std::advance(selected_dir, selected_dir_index); + current_root = selected_dir->second.get(); + depth++; + } + + return fmt::format("{}/{}", current_root->path(), new_dir_name); +} + +std::string FileTree::newFilePath() { + std::string new_file_name = fmt::format("file_{}", file_id++); + unsigned int depth = 0; + unsigned int rand_selected_depth = rand() % _max_depth; + + auto current_root = root.get(); + + while (depth < rand_selected_depth) { + auto current_dir_count = current_root->folders.size(); + if (current_dir_count == 0) { + break; + } + auto selected_dir_index = rand() % current_dir_count; + auto selected_dir = current_root->folders.begin(); + std::advance(selected_dir, selected_dir_index); + current_root = selected_dir->second.get(); + depth++; + } + + return fmt::format("{}/{}", current_root->path(), new_file_name); } \ No newline at end of file diff --git a/source/scenarios/aging.cpp b/source/scenarios/aging.cpp index c221ca7..3ad79aa 100644 --- a/source/scenarios/aging.cpp +++ b/source/scenarios/aging.cpp @@ -2,14 +2,18 @@ #include #include #include +#include + +#include +#include AgingScenario::AgingScenario() { _name = "aging"; _description = "Scenario for testing filesystem aging."; addParameter(Parameter("d", "directory", "", "/tmp/filestorm/")); addParameter(Parameter("r", "depth", "Max directory depth", "5")); - addParameter(Parameter("n", "ndirs", "Max number of dirs per level", "false")); - addParameter(Parameter("f", "nfiles", "Max number of files per level", "false")); + addParameter(Parameter("n", "ndirs", "Max number of dirs", "200")); + addParameter(Parameter("f", "nfiles", "Max number of files", "2000")); addParameter(Parameter("m", "fs-capacity", "Max overall filesystem size", "full")); // TODO: add support for this addParameter(Parameter("s", "fsize", "Max file size", "full")); addParameter(Parameter("p", "sdist", "File size probabilistic distribution", "1000")); @@ -19,62 +23,119 @@ AgingScenario::AgingScenario() { AgingScenario::~AgingScenario() {} void AgingScenario::run() { - FileTree tree(getParameter("directory").get_string()); - - std::function pC = [&]() { - // CAF - Capacity Awareness Factor - auto fs_status = fs_utils::get_fs_status(getParameter("directory").get_string()); - return CAF(fs_status.capacity / (fs_status.capacity - fs_status.free)); - }; - std::function pD = [&]() { return 0.1 * (1 / pC()); }; - std::function pA = [&]() { return 1 - pC() - pD(); }; - std::function pAM = []() { return 0.1; }; - std::function pAB = pC; - std::function pAS = [&]() { return 1 - pAM() - pAB(); }; - std::function pDD = []() { return 0.01; }; - std::function pDF = [&]() { return 1 - pDD(); }; - std::function pCF = [&]() { return (tree.getDirectoryCount() / getParameter("ndirs").get_int()); }; - std::function pCD = [&]() { return 1 - pCF(); }; - std::function p1 = [=]() { return 1.0; }; - - std::map states; - states.emplace("S", State("S")); - states.emplace("CREATE", State("CREATE")); - states.emplace("ALTER", State("ALTER")); - states.emplace("DELETE", State("DELETE")); - states.emplace("CREATE_FILE", State("CREATE_FILE")); - states.emplace("CREATE_DIR", State("CREATE_DIR")); - states.emplace("ALTER_SMALLER", State("ALTER_SMALLER")); - states.emplace("ALTER_BIGGER", State("ALTER_BIGGER")); - states.emplace("DELETE_FILE", State("DELETE_FILE")); - states.emplace("DELETE_DIR", State("DELETE_DIR")); - states.emplace("END", State("END")); - + FileTree tree(getParameter("directory").get_string(), getParameter("depth").get_int()); std::map transtions; - transtions.emplace("S->CREATE", Transition(states.at("S"), states.at("CREATE"), pC)); - transtions.emplace("S->ALTER", Transition(states.at("S"), states.at("ALTER"), pA)); - transtions.emplace("S->DELETE", Transition(states.at("S"), states.at("DELETE"), pD)); - transtions.emplace("CREATE->CREATE_FILE", Transition(states.at("CREATE"), states.at("CREATE_FILE"), pCF)); - transtions.emplace("CREATE->CREATE_DIR", Transition(states.at("CREATE"), states.at("CREATE_DIR"), pCD)); - transtions.emplace("ALTER->ALTER_SMALLER", Transition(states.at("ALTER"), states.at("ALTER_SMALLER"), pAS)); - transtions.emplace("ALTER->ALTER_BIGGER", Transition(states.at("ALTER"), states.at("ALTER_BIGGER"), pAB)); - transtions.emplace("DELETE->DELETE_FILE", Transition(states.at("DELETE"), states.at("DELETE_FILE"), pDF)); - transtions.emplace("DELETE->DELETE_DIR", Transition(states.at("DELETE"), states.at("DELETE_DIR"), pDD)); - transtions.emplace("CREATE_FILE->END", Transition(states.at("CREATE_FILE"), states.at("END"), p1)); - transtions.emplace("CREATE_DIR->END", Transition(states.at("CREATE_DIR"), states.at("END"), p1)); - transtions.emplace("ALTER_SMALLER->END", Transition(states.at("ALTER_SMALLER"), states.at("END"), p1)); - transtions.emplace("ALTER_BIGGER->END", Transition(states.at("ALTER_BIGGER"), states.at("END"), p1)); - transtions.emplace("DELETE_FILE->END", Transition(states.at("DELETE_FILE"), states.at("END"), p1)); - transtions.emplace("DELETE_DIR->END", Transition(states.at("DELETE_DIR"), states.at("END"), p1)); - transtions.emplace("END->S", Transition(states.at("END"), states.at("S"), p1)); + transtions.emplace("S->CREATE", Transition(S, CREATE, "pC")); + transtions.emplace("S->ALTER", Transition(S, ALTER, "pA")); + transtions.emplace("S->DELETE", Transition(S, DELETE, "pD")); + transtions.emplace("CREATE->CREATE_FILE", Transition(CREATE, CREATE_FILE, "pCF")); + transtions.emplace("CREATE->CREATE_DIR", Transition(CREATE, CREATE_DIR, "pCD")); + transtions.emplace("ALTER->ALTER_SMALLER", Transition(ALTER, ALTER_SMALLER, "pAS")); + transtions.emplace("ALTER->ALTER_BIGGER", Transition(ALTER, ALTER_BIGGER, "pAB")); + transtions.emplace("ALTER->ALTER_METADATA", Transition(ALTER, ALTER_METADATA, "pAM")); + transtions.emplace("DELETE->DELETE_FILE", Transition(DELETE, DELETE_FILE, "pDF")); + transtions.emplace("DELETE->DELETE_DIR", Transition(DELETE, DELETE_DIR, "pDD")); + transtions.emplace("CREATE_FILE->END", Transition(CREATE_FILE, END, "p1")); + transtions.emplace("CREATE_DIR->END", Transition(CREATE_DIR, END, "p1")); + transtions.emplace("ALTER_SMALLER->END", Transition(ALTER_SMALLER, END, "p1")); + transtions.emplace("ALTER_BIGGER->END", Transition(ALTER_BIGGER, END, "p1")); + transtions.emplace("ALTER_METADATA->END", Transition(ALTER_METADATA, END, "p1")); + transtions.emplace("DELETE_FILE->END", Transition(DELETE_FILE, END, "p1")); + transtions.emplace("DELETE_DIR->END", Transition(DELETE_DIR, END, "p1")); + transtions.emplace("END->S", Transition(END, S, "p1")); int iteration = 0; - progressbar bar(getParameter("iterations").get_int()); + // progressbar bar(getParameter("iterations").get_int()); - ProbabilisticStateMachine psm(transtions, states.at("S")); + ProbabilisticStateMachine psm(transtions, S); + std::map probabilities; while (iteration < getParameter("iterations").get_int()) { - bar.update(); + compute_probabilities(probabilities, tree); + psm.performTransition(probabilities); + switch (psm.getCurrentState()) { + case S: + spdlog::debug("S"); + break; + case CREATE: + spdlog::debug("CREATE"); + break; + case ALTER: + spdlog::debug("ALTER"); + break; + case DELETE: + spdlog::debug("DELETE"); + break; + case CREATE_FILE: { + auto new_file_path = tree.newFilePath(); + spdlog::debug("CREATE_FILE {}", new_file_path); + FileTree::Node* file_node = tree.mkfile(new_file_path); + spdlog::debug(fmt::format("CREATE_FILE {} {}", new_file_path, file_node->path(true))); + + break; + } + case CREATE_DIR: { + auto new_dir_path = tree.newDirectoryPath(); + spdlog::debug("CREATE_DIR {}", new_dir_path); + FileTree::Node* dir_node = tree.mkdir(new_dir_path); + spdlog::debug(fmt::format("CREATE_DIR {}", new_dir_path, dir_node->path(true))); + break; + } + case ALTER_SMALLER: + spdlog::debug("ALTER_SMALLER"); + break; + case ALTER_BIGGER: + spdlog::debug("ALTER_BIGGER"); + break; + case ALTER_METADATA: + spdlog::debug("ALTER_METADATA"); + break; + case DELETE_FILE: + spdlog::debug("DELETE_FILE"); + break; + case DELETE_DIR: + spdlog::debug("DELETE_DIR"); + break; + case END: + spdlog::debug("END"); + iteration++; + break; + + default: + break; + } + + // bar.update(); iteration++; } +} + +void AgingScenario::compute_probabilities(std::map& probabilities, FileTree& tree) { + probabilities.clear(); + auto fs_status = fs_utils::get_fs_status(getParameter("directory").get_string()); + + // double CAF(double x) { return sqrt(1 - (x * x)); } + // spdlog::debug("Capacity: {}, free: {}", fs_status.capacity, fs_status.free); + // spdlog::debug("CAF input: {}", ((float(fs_status.capacity - fs_status.free) / float(fs_status.capacity)))); + + double caf = CAF((float(fs_status.capacity - fs_status.free) / float(fs_status.capacity))); + + probabilities["pC"] = caf; + probabilities["pD"] = 0.1 * (1.0 - caf); + probabilities["pA"] = 1 - caf - probabilities["pD"]; + std::string logMessage = fmt::format("pC: {:.2f}, pD: {:.2f}, pA: {:.2f}, sum p {:.2f} ", probabilities["pC"], probabilities["pD"], probabilities["pA"], + probabilities["pC"] + probabilities["pD"] + probabilities["pA"]); + probabilities["pAM"] = 0.1; + probabilities["pAB"] = caf; + probabilities["pAS"] = 1 - probabilities["pAM"] - probabilities["pAB"]; + logMessage += fmt::format("pAM: {:.2f}, pAB: {:.2f}, pAS: {:.2f}, sum pA {:.2f} ", probabilities["pAM"], probabilities["pAB"], probabilities["pAS"], + probabilities["pAM"] + probabilities["pAB"] + probabilities["pAS"]); + probabilities["pDD"] = 0.01; + probabilities["pDF"] = 1 - probabilities["pDD"]; + logMessage += fmt::format("pDD: {:.2f}, pDF: {:.2f}, sum pD {:.2f} ", probabilities["pDD"], probabilities["pDF"], probabilities["pDD"] + probabilities["pDF"]); + probabilities["pCF"] = (tree.getDirectoryCount() / getParameter("ndirs").get_int()); + probabilities["pCD"] = 1 - probabilities["pCF"]; + logMessage += fmt::format("pCF: {:.2f}, pCD: {:.2f}, sum pC {:.2f} ", probabilities["pCF"], probabilities["pCD"], probabilities["pCF"] + probabilities["pCD"]); + probabilities["p1"] = 1.0; + // spdlog::debug(logMessage); } \ No newline at end of file diff --git a/source/utils.cpp b/source/utils.cpp index cd1a120..d401136 100644 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -11,4 +11,40 @@ std::vector split(const std::string& str, char delimiter) { result.push_back(token); } return result; -} \ No newline at end of file +} + +std::string strip(const std::string& str) { + size_t start = 0; + size_t end = str.length(); + + // Find the index of the first non-whitespace character from the beginning + while (start < end && std::isspace(str[start])) { + ++start; + } + + // Find the index of the first non-whitespace character from the end + while (end > start && std::isspace(str[end - 1])) { + --end; + } + + // Return the stripped substring + return str.substr(start, end - start); +} + +std::string strip(const std::string& str, const char stripChar) { + size_t start = 0; + size_t end = str.length(); + + // Find the index of the first non-stripChar character from the beginning + while (start < end && str[start] == stripChar) { + ++start; + } + + // Find the index of the first non-stripChar character from the end + while (end > start && str[end - 1] == stripChar) { + --end; + } + + // Return the stripped substring + return str.substr(start, end - start); +} diff --git a/standalone/source/config.cpp b/standalone/source/config.cpp index 3afb842..17a7b2a 100644 --- a/standalone/source/config.cpp +++ b/standalone/source/config.cpp @@ -22,5 +22,5 @@ Scenario* Config::get_scenario(const std::string& name) const { return scenario; } } - throw std::invalid_argument(fmt::format("Scenario {} not found. Supported scenarios are {}", name, get_supported_scenarios_as_string())); + throw BadScenarioSelected(fmt::format("Scenario {} not found. Supported scenarios are {}", name, get_supported_scenarios_as_string())); } \ No newline at end of file diff --git a/standalone/source/config.h b/standalone/source/config.h index 6d2e697..e0a5207 100644 --- a/standalone/source/config.h +++ b/standalone/source/config.h @@ -3,6 +3,8 @@ #include #include +#include +#include #include class Config { @@ -19,4 +21,12 @@ class Config { Scenario* get_scenario(const std::string& name) const; }; -extern const Config config; \ No newline at end of file +extern const Config config; + +// Custom exception class + +class BadScenarioSelected : public std::runtime_error { +public: + BadScenarioSelected(const char* message) : std::runtime_error(message) {} + BadScenarioSelected(const std::string& message) : std::runtime_error(message) {} +}; diff --git a/standalone/source/main.cpp b/standalone/source/main.cpp index 42dad49..308bec1 100644 --- a/standalone/source/main.cpp +++ b/standalone/source/main.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include // Include for getopts @@ -19,28 +20,14 @@ void displayHelp() { } std::cout << "Options:\n" << " -h, --help Display this help message\n" - << " -v, --version Display version information\n"; + << " -v, --version Display version information\n" + << " -l, --log level Set the log level (trace, debug, info, warn, error, critical, off)" << std::endl; } void displayVersion() { std::cout << FILESTORM_VERSION << std::endl; } auto main(int argc, char** argv) -> int { spdlog::set_level(spdlog::level::debug); - int option; - while ((option = getopt(argc, argv, "hv")) != -1) { - switch (option) { - case 'h': - displayHelp(); - return 0; - case 'v': - displayVersion(); - return 0; - default: - spdlog::error("Error: Invalid option."); - displayHelp(); - return 1; - } - } if (optind >= argc) { spdlog::error("Error: Insufficient arguments."); @@ -55,8 +42,31 @@ auto main(int argc, char** argv) -> int { char** new_argv = argv + optind; scenario->setup(new_argc, new_argv); scenario->run(); - } catch (const std::invalid_argument& e) { - spdlog::error("Error: {}", e.what()); + } catch (const BadScenarioSelected& e) { + spdlog::error("Error on main: {}", e.what()); + const struct option long_options[] = {{"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"log", required_argument, NULL, 'l'}, {NULL, 0, NULL, 0}}; + int opt; + while ((opt = getopt_long(argc, argv, "hvl:", long_options, NULL)) != -1) { + switch (opt) { + case 'h': + // Display help message + displayHelp(); + return 0; + case 'v': + // Display version information + displayVersion(); + return 0; + case 'l': + // Set the log level + spdlog::set_level(spdlog::level::from_str(optarg)); + break; + case '?': + // Handle unknown options or missing arguments + break; + default: + break; + } + } return 1; } diff --git a/test/source/tests_filetree.cpp b/test/source/tests_filetree.cpp index 2481855..2549467 100644 --- a/test/source/tests_filetree.cpp +++ b/test/source/tests_filetree.cpp @@ -33,7 +33,8 @@ TEST_CASE("Testing FileTree") { SUBCASE("Removing nodes") { auto dir1 = tree.addDirectory(tree.getRoot(), "dir1"); tree.remove(dir1); - CHECK(tree.getRoot()->children.empty()); + CHECK(tree.getRoot()->files.empty()); + CHECK(tree.getRoot()->folders.empty()); } } @@ -70,7 +71,8 @@ TEST_CASE("Testing FileTree with multiple levels") { auto dir1 = tree.addDirectory(tree.getRoot(), "dir1"); auto subdir1 = tree.addDirectory(dir1, "subdir1"); tree.remove(subdir1); - CHECK(dir1->children.empty()); + CHECK(dir1->files.empty()); + CHECK(dir1->folders.empty()); } } @@ -84,7 +86,8 @@ TEST_CASE("FileTree mkdir") { CHECK(createdDir->type == FileTree::Type::DIRECTORY); // Check that the parent directory has the child - CHECK(fileTree.getNode("/")->children.count("newDir") == 1); + CHECK(fileTree.getNode("/")->folders.count("newDir") == 1); + CHECK(fileTree.getNode("/")->files.count("newDir") == 0); } SUBCASE("Create directory recursively") { @@ -94,8 +97,8 @@ TEST_CASE("FileTree mkdir") { CHECK(createdDir->type == FileTree::Type::DIRECTORY); // Check that all parent directories have the child - CHECK(fileTree.getNode("/")->children.count("newDir2") == 1); - CHECK(fileTree.getNode("newDir2")->children.count("anotherDir") == 1); + CHECK(fileTree.getNode("/")->folders.count("newDir2") == 1); + CHECK(fileTree.getNode("newDir2")->folders.count("anotherDir") == 1); } SUBCASE("Create directory with existing path") { @@ -121,7 +124,8 @@ TEST_CASE("FileTree mkfile") { CHECK(createdFile->type == FileTree::Type::FILE); // Check that the parent directory has the file - CHECK(fileTree.getNode("/")->children.count("newFile.txt") == 1); + CHECK(fileTree.getNode("/")->files.count("newFile.txt") == 1); + CHECK(fileTree.getNode("/")->folders.count("newFile.txt") == 0); } SUBCASE("Create file with existing path") {