From 1eb338789c392dfa0d4a3c45bc8a54c7fd988398 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 5 Feb 2025 22:39:10 +0100 Subject: [PATCH] added first creation scripts but currently this is not really well working. --- src/prodir/cpp/bindings.cpp | 63 ++++++++-- src/prodir/cpp/tree_structurer.cpp | 187 +++++++++++++++++++++++++++-- src/prodir/cpp/tree_structurer.hpp | 32 ++++- 3 files changed, 261 insertions(+), 21 deletions(-) diff --git a/src/prodir/cpp/bindings.cpp b/src/prodir/cpp/bindings.cpp index 6aa031a..625c630 100644 --- a/src/prodir/cpp/bindings.cpp +++ b/src/prodir/cpp/bindings.cpp @@ -18,12 +18,11 @@ static PyObject* get_structure(PyObject* self, PyObject* args) { if (!PyArg_ParseTuple(args, "|s", &path)) { return NULL; } - if (g_tree_structurer == nullptr) { PyErr_SetString(TreeStructurerError, "TreeStructurer not initialized"); return NULL; } - + try { std::string path_str = path ? path : ""; std::vector structure = g_tree_structurer->get_directory_structure(path_str); @@ -38,27 +37,77 @@ static PyObject* get_structure(PyObject* self, PyObject* args) { } } +static PyObject* create_structure_from_file(PyObject* self, PyObject* args) { + const char* structure_file = nullptr; + const char* target_path = nullptr; + + if (!PyArg_ParseTuple(args, "s|s", &structure_file, &target_path)) { + return NULL; + } + + if (g_tree_structurer == nullptr) { + PyErr_SetString(TreeStructurerError, "TreeStructurer not initialized"); + return NULL; + } + + try { + std::string target_path_str = target_path ? target_path : ""; + g_tree_structurer->create_structure_from_file(structure_file, target_path_str); + Py_RETURN_NONE; + } catch (const std::exception& e) { + PyErr_SetString(TreeStructurerError, e.what()); + return NULL; + } +} + +static PyObject* create_structure_from_string(PyObject* self, PyObject* args) { + const char* structure_str = nullptr; + const char* target_path = nullptr; + + if (!PyArg_ParseTuple(args, "s|s", &structure_str, &target_path)) { + return NULL; + } + + if (g_tree_structurer == nullptr) { + PyErr_SetString(TreeStructurerError, "TreeStructurer not initialized"); + return NULL; + } + + try { + std::string target_path_str = target_path ? target_path : ""; + g_tree_structurer->create_structure_from_string(structure_str, target_path_str); + Py_RETURN_NONE; + } catch (const std::exception& e) { + PyErr_SetString(TreeStructurerError, e.what()); + return NULL; + } +} + static PyMethodDef TreeStructurerMethods[] = { - {"create_tree_structurer", create_tree_structurer, METH_NOARGS, + {"create_tree_structurer", create_tree_structurer, METH_NOARGS, "Create a new TreeStructurer instance"}, {"get_structure", get_structure, METH_VARARGS, "Get the directory structure for the given path"}, + {"create_structure_from_file", create_structure_from_file, METH_VARARGS, + "Create directory structure from a file containing the structure description"}, + {"create_structure_from_string", create_structure_from_string, METH_VARARGS, + "Create directory structure from a string containing the structure description"}, {NULL, NULL, 0, NULL} }; static struct PyModuleDef tree_structurer_module = { PyModuleDef_HEAD_INIT, "_tree_structurer", // Changed module name to match Python import - "Module for analyzing directory structures", + "Module for analyzing and creating directory structures", -1, TreeStructurerMethods }; -PyMODINIT_FUNC PyInit__tree_structurer(void) { // Changed function name to match module name +PyMODINIT_FUNC PyInit__tree_structurer(void) { PyObject* m = PyModule_Create(&tree_structurer_module); if (m == NULL) return NULL; - + TreeStructurerError = PyErr_NewException("tree_structurer.error", NULL, NULL); Py_XINCREF(TreeStructurerError); if (PyModule_AddObject(m, "error", TreeStructurerError) < 0) { @@ -67,6 +116,6 @@ PyMODINIT_FUNC PyInit__tree_structurer(void) { // Changed function name to matc Py_DECREF(m); return NULL; } - + return m; } \ No newline at end of file diff --git a/src/prodir/cpp/tree_structurer.cpp b/src/prodir/cpp/tree_structurer.cpp index 2ef3be2..67dffce 100644 --- a/src/prodir/cpp/tree_structurer.cpp +++ b/src/prodir/cpp/tree_structurer.cpp @@ -1,6 +1,9 @@ #include "tree_structurer.hpp" #include #include +#include +#include +#include namespace fs = std::filesystem; @@ -10,22 +13,21 @@ bool TreeStructurer::should_ignore_dir(const std::string& dirname) { ".git", ".idea", ".vscode", "__pycache__", "**pycache**" }; - // Check for __main__.py or __init__.py files if (!dirname.empty() && (dirname == "__main__.py" || dirname == "__init__.py")) { - return false; // Do not ignore these files + return false; } if (std::find(ignore_list.begin(), ignore_list.end(), dirname) != ignore_list.end()) { - return true; - } + return true; + } if (!dirname.empty()) { if (dirname[0] == '.' || dirname[0] == '_') { return true; } if (dirname.find("__") == 0 && dirname.find("__", 2) != std::string::npos) { - return true; - } + return true; + } } return false; @@ -37,10 +39,9 @@ bool TreeStructurer::should_ignore_file(const std::string& filename) { ".o", ".obj", ".a", ".lib" }; - // Check for __main__.py or __init__.py files if (!filename.empty() && (filename == "__main__.py" || filename == "__init__.py")) { - return false; // Do not ignore these files - } + return false; +} if (!filename.empty() && (filename[0] == '.' || filename[0] == '_')) { return true; @@ -55,7 +56,6 @@ bool TreeStructurer::should_ignore_file(const std::string& filename) { return false; } -// Add the missing get_relative_path implementation std::string TreeStructurer::get_relative_path(const fs::path& path, const fs::path& base) { fs::path rel = fs::relative(path, base); return rel.string(); @@ -76,7 +76,6 @@ std::vector TreeStructurer::get_filtered_paths(const fs::path& start) paths.push_back(start); - // Check if directory is empty bool is_empty = fs::directory_iterator(start) == fs::directory_iterator(); if (is_empty) { throw std::runtime_error("Directory is empty: " + start.string()); @@ -115,7 +114,6 @@ std::vector TreeStructurer::get_filtered_paths(const fs::path& start) std::vector TreeStructurer::get_directory_structure(const std::string& startpath) { std::vector result; - // Normalize the input path by removing ./ or .\ prefix if present std::string normalized_path = startpath; if (startpath.substr(0, 2) == ".\\" || startpath.substr(0, 2) == "./") { normalized_path = startpath.substr(2); @@ -171,4 +169,169 @@ std::vector TreeStructurer::get_directory_structure(const std::stri } return result; +} + +void TreeStructurer::create_structure_from_file(const std::string& filepath, const std::string& target_path) { + std::vector lines = read_structure_file(filepath); + validate_structure(lines); + TreeNode root = build_tree_from_lines(lines); + create_node(root, target_path); +} + +void TreeStructurer::create_structure_from_string(const std::string& structure, const std::string& target_path) { + std::istringstream iss(structure); + std::vector lines; + std::string line; + + while (std::getline(iss, line)) { + if (!line.empty()) { + lines.push_back(line); + } + } + + validate_structure(lines); + TreeNode root = build_tree_from_lines(lines); + create_node(root, target_path); +} + +void TreeStructurer::validate_structure(const std::vector& lines) { + if (lines.empty()) { + throw std::runtime_error("Empty structure provided"); + } + + int prev_indent = -1; + for (const auto& line : lines) { + int current_indent = get_indent_level(line); + if (current_indent > prev_indent + 1) { + throw std::runtime_error("Invalid indentation level in structure"); + } + prev_indent = current_indent; + } +} + +TreeStructurer::TreeNode TreeStructurer::build_tree_from_lines(const std::vector& lines) { + TreeNode root{"root", true, {}}; + std::vector stack{&root}; + size_t prev_indent = 0; + + for (const auto& line : lines) { + size_t current_indent = get_indent_level(line); + + while (current_indent <= prev_indent && stack.size() > 1) { + stack.pop_back(); + prev_indent--; + } + + TreeNode new_node = parse_structure_line(line, current_indent); + stack.back()->children.push_back(new_node); + + if (!new_node.is_file) { + stack.push_back(&(stack.back()->children.back())); + } + + prev_indent = current_indent; + } + + return root; +} + +void TreeStructurer::create_node(const TreeNode& node, const std::filesystem::path& current_path) { + std::filesystem::path new_path = current_path / node.name; + + if (node.is_file) { + create_file(new_path); + } else { + create_directory(new_path); + for (const auto& child : node.children) { + create_node(child, new_path); + } + } +} + +void TreeStructurer::create_directory(const std::filesystem::path& path) { + if (!std::filesystem::exists(path)) { + if (!std::filesystem::create_directory(path)) { + throw std::runtime_error("Failed to create directory: " + path.string()); + } + } +} + +void TreeStructurer::create_file(const std::filesystem::path& path) { + if (!std::filesystem::exists(path)) { + std::ofstream file(path); + if (!file.is_open()) { + throw std::runtime_error("Failed to create file: " + path.string()); + } + } +} + +TreeStructurer::TreeNode TreeStructurer::parse_structure_line(const std::string& line, size_t indent_level) { + size_t name_start = 0; + for (size_t i = 0; i < line.length(); ++i) { + if (line[i] != ' ' && + static_cast(line[i]) != INDENT_MARKER_PIPE && + static_cast(line[i]) != INDENT_MARKER_DASH && + static_cast(line[i]) != INDENT_MARKER_END && + line[i] != '-') { + name_start = i; + break; + } + } + + std::string name = line.substr(name_start); + + name.erase(0, name.find_first_not_of(" ")); + name.erase(name.find_last_not_of(" ") + 1); + + bool is_file = !is_directory_marker(name); + + if (!is_file && name.back() == '/') { + name.pop_back(); + } + + return TreeNode{name, is_file, {}}; +} + +size_t TreeStructurer::get_indent_level(const std::string& line) { + size_t level = 0; + for (size_t i = 0; i < line.length(); ++i) { + if (line[i] == ' ') { + continue; + } + if (line[i] == '-' || + static_cast(line[i]) == INDENT_MARKER_PIPE || + static_cast(line[i]) == INDENT_MARKER_DASH || + static_cast(line[i]) == INDENT_MARKER_END) { + level++; + } else { + break; + } + } + return level / 4; +} + +bool TreeStructurer::is_directory_marker(const std::string& line) { + return !line.empty() && line.back() == DIRECTORY_MARKER; +} + +std::string TreeStructurer::create_indent(size_t level) { + return std::string(level * 4, ' '); +} + +std::vector TreeStructurer::read_structure_file(const std::string& filepath) { + std::vector lines; + std::ifstream file(filepath); + + if (!file.is_open()) { + throw std::runtime_error("Failed to open file: " + filepath); + } + + std::string line; + while (std::getline(file, line)) { + if (!line.empty()) { + lines.push_back(line); + } + } + + return lines; } \ No newline at end of file diff --git a/src/prodir/cpp/tree_structurer.hpp b/src/prodir/cpp/tree_structurer.hpp index 06c51be..ddcdee0 100644 --- a/src/prodir/cpp/tree_structurer.hpp +++ b/src/prodir/cpp/tree_structurer.hpp @@ -2,16 +2,44 @@ #include #include #include +#include +#include +#include class TreeStructurer { public: TreeStructurer() = default; + std::vector get_directory_structure(const std::string& startpath = std::filesystem::current_path().string()); + + void create_structure_from_file(const std::string& filepath, const std::string& target_path = std::filesystem::current_path().string()); + void create_structure_from_string(const std::string& structure, const std::string& target_path = std::filesystem::current_path().string()); private: + struct TreeNode { + std::string name; + bool is_file; + std::vector children; + }; + bool should_ignore_dir(const std::string& dirname); bool should_ignore_file(const std::string& filename); - std::string create_indent(int level); + std::string create_indent(size_t level); std::string get_relative_path(const std::filesystem::path& path, const std::filesystem::path& base); std::vector get_filtered_paths(const std::filesystem::path& start); -}; \ No newline at end of file + + TreeNode parse_structure_line(const std::string& line, size_t indent_level); + TreeNode build_tree_from_lines(const std::vector& lines); + void create_node(const TreeNode& node, const std::filesystem::path& current_path); + size_t get_indent_level(const std::string& line); + bool is_directory_marker(const std::string& line); + void create_directory(const std::filesystem::path& path); + void create_file(const std::filesystem::path& path); + std::vector read_structure_file(const std::string& filepath); + void validate_structure(const std::vector& lines); + + static const unsigned char DIRECTORY_MARKER = '/'; + static const unsigned char INDENT_MARKER_PIPE = '|'; + static const unsigned char INDENT_MARKER_DASH = '+'; + static const unsigned char INDENT_MARKER_END = '\\'; +};