updated getting dir
Gitea Actions For Tree-Structurer / Explore-Gitea-Actions (push) Successful in 23s Details

This commit is contained in:
Falko Victor Habel 2025-02-07 16:30:29 +01:00
parent 16f0e89e91
commit 0d01f188e7
2 changed files with 126 additions and 72 deletions

View File

@ -18,16 +18,16 @@ bool TreeStructurer::should_ignore_dir(const std::string& dirname) {
}
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;
@ -41,7 +41,7 @@ bool TreeStructurer::should_ignore_file(const std::string& filename) {
if (!filename.empty() && (filename == "__main__.py" || filename == "__init__.py")) {
return false;
}
}
if (!filename.empty() && (filename[0] == '.' || filename[0] == '_')) {
return true;
@ -171,6 +171,10 @@ std::vector<std::string> TreeStructurer::get_directory_structure(const std::stri
return result;
}
//
// UPDATED: Instead of processing the lines “on the fly,” we now build the tree and then create the project.
// This also allows the same parsing logic to be reused by create_structure_from_string().
//
void TreeStructurer::create_structure_from_file(const std::string& filepath, const std::string& target_path) {
std::vector<std::string> lines = read_structure_file(filepath);
validate_structure(lines);
@ -194,6 +198,9 @@ void TreeStructurer::create_structure_from_string(const std::string& structure,
create_node(root, target_path);
}
//
// UPDATED: Now using a Unicodeaware (tokenbased) indent calculation.
//
void TreeStructurer::validate_structure(const std::vector<std::string>& lines) {
if (lines.empty()) {
throw std::runtime_error("Empty structure provided");
@ -201,7 +208,7 @@ void TreeStructurer::validate_structure(const std::vector<std::string>& lines) {
int prev_indent = -1;
for (const auto& line : lines) {
int current_indent = get_indent_level(line);
int current_indent = static_cast<int>(get_indent_level(line));
if (current_indent > prev_indent + 1) {
throw std::runtime_error("Invalid indentation level in structure");
}
@ -209,64 +216,110 @@ void TreeStructurer::validate_structure(const std::vector<std::string>& lines) {
}
}
//
// UPDATED: Parse the indent level by looking for 4character tokens.
//
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<unsigned char>(line[i]) == INDENT_MARKER_PIPE ||
static_cast<unsigned char>(line[i]) == INDENT_MARKER_DASH ||
static_cast<unsigned char>(line[i]) == INDENT_MARKER_END) {
level++;
} else {
break;
}
size_t pos = 0;
// Look for “blank” or vertical bar segments (each 4 characters)
while (pos + 4 <= line.size() && (line.compare(pos, 4, " ") == 0 || line.compare(pos, 4, "") == 0)) {
level++;
pos += 4;
}
return level / 4;
// Then look for the branch indicator at this level
if (pos + 4 <= line.size() && (line.compare(pos, 4, "└── ") == 0 || line.compare(pos, 4, "├── ") == 0)) {
level++;
}
return level;
}
//
// UPDATED: Build a tree of nodes from the structure lines using the same tokenbased parsing.
// The first (root) line is expected to be a directory (with a trailing '/').
TreeStructurer::TreeNode TreeStructurer::build_tree_from_lines(const std::vector<std::string>& lines) {
TreeNode root{"root", false, {}};
std::vector<TreeNode*> stack{&root};
size_t prev_indent = 0;
if (lines.empty()) {
throw std::runtime_error("Empty structure provided");
}
for (const auto& line : lines) {
// Process the first line as the root.
std::string first_line = lines[0];
size_t pos = 0;
// Skip any indent tokens (if any)
while (pos + 4 <= first_line.size() && (first_line.compare(pos, 4, " ") == 0 || first_line.compare(pos, 4, "") == 0)) {
pos += 4;
}
if (pos + 4 <= first_line.size() && (first_line.compare(pos, 4, "└── ") == 0 || first_line.compare(pos, 4, "├── ") == 0)) {
pos += 4;
}
std::string root_name = first_line.substr(pos);
if (root_name.empty() || root_name.back() != DIRECTORY_MARKER) {
throw std::runtime_error("Root must be a directory (ending with '" + std::string(1, DIRECTORY_MARKER) + "')");
}
root_name.pop_back(); // Remove trailing '/'
TreeNode root{root_name, false, {}};
std::vector<TreeNode*> stack;
stack.push_back(&root);
// Process remaining lines.
for (size_t i = 1; i < lines.size(); ++i) {
const std::string& line = lines[i];
if (line.empty()) continue;
size_t current_indent = get_indent_level(line);
while (current_indent <= prev_indent && stack.size() > 1) {
stack.pop_back();
prev_indent--;
size_t pos = 0;
size_t current_level = 0;
// Process any “blank” or vertical indent segments (4 characters each)
while (pos + 4 <= line.size() && (line.compare(pos, 4, " ") == 0 || line.compare(pos, 4, "") == 0)) {
current_level++;
pos += 4;
}
// Process the branch token if present
if (pos + 4 <= line.size() && (line.compare(pos, 4, "└── ") == 0 || line.compare(pos, 4, "├── ") == 0)) {
current_level++;
pos += 4;
}
std::string name = line.substr(pos);
if (name.empty()) continue;
bool is_file = true;
if (name.back() == DIRECTORY_MARKER) {
is_file = false;
name.pop_back();
}
name = sanitize_path(name);
if (name.empty()) continue;
TreeNode new_node = parse_structure_line(line, current_indent);
if (!new_node.name.empty()) {
stack.back()->children.push_back(new_node);
if (!new_node.is_file) {
stack.push_back(&(stack.back()->children.back()));
}
prev_indent = current_indent;
// Adjust the stack to the proper level.
while (stack.size() > current_level) {
stack.pop_back();
}
if (stack.empty()) {
throw std::runtime_error("Invalid indentation structure in the file.");
}
TreeNode new_node{name, is_file, {}};
stack.back()->children.push_back(new_node);
if (!is_file) {
// For a directory, push the new node onto the stack.
stack.push_back(&stack.back()->children.back());
}
}
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
std::filesystem::path parent_path = new_path.parent_path();
if (!std::filesystem::exists(parent_path)) {
std::filesystem::create_directories(parent_path);
}
create_file(new_path);
} else {
// Create directory
create_directory(new_path);
// Recursively create children
for (const auto& child : node.children) {
create_node(child, new_path);
}
@ -275,9 +328,7 @@ void TreeStructurer::create_node(const TreeNode& node, const std::filesystem::pa
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());
}
std::filesystem::create_directories(path);
}
}
@ -287,30 +338,33 @@ void TreeStructurer::create_file(const std::filesystem::path& path) {
if (!file.is_open()) {
throw std::runtime_error("Failed to create file: " + path.string());
}
file.close();
}
}
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<unsigned char>(line[i]) != INDENT_MARKER_PIPE &&
static_cast<unsigned char>(line[i]) != INDENT_MARKER_DASH &&
static_cast<unsigned char>(line[i]) != INDENT_MARKER_END &&
line[i] != '-' &&
line[i] != '' &&
line[i] != '') {
name_start = i;
break;
std::string TreeStructurer::sanitize_path(const std::string& path) {
std::string result;
for (char c : path) {
// Allow alphanumeric, common punctuation, the directory separator, and our tree tokens.
if (std::isalnum(static_cast<unsigned char>(c)) ||
c == '.' || c == '_' || c == '-' || c == '/' ||
c == TREE_PIPE || c == TREE_BRANCH || c == TREE_CORNER || c == TREE_DASH) {
result += c;
}
}
return result;
}
TreeStructurer::TreeNode TreeStructurer::parse_structure_line(const std::string& line, size_t indent_level) {
size_t name_start = line.find_first_not_of(" │├└─");
if (name_start == std::string::npos) {
return TreeNode{"", true, {}};
}
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 = name.empty() || name.back() != '/';
name = sanitize_path(name);
bool is_file = !name.empty() && name.back() != DIRECTORY_MARKER;
if (!is_file) {
name.pop_back();
}
@ -342,4 +396,4 @@ std::vector<std::string> TreeStructurer::read_structure_file(const std::string&
}
return lines;
}
}

View File

@ -17,9 +17,9 @@ public:
private:
struct TreeNode {
std::string name;
bool is_file;
std::vector<TreeNode> children;
std::string name;
bool is_file;
std::vector<TreeNode> children;
};
bool should_ignore_dir(const std::string& dirname);
@ -30,17 +30,17 @@ private:
size_t get_indent_level(const std::string& line);
TreeNode parse_structure_line(const std::string& line, size_t indent_level);
TreeNode build_tree_from_lines(const std::vector<std::string>& lines);
void create_node(const TreeNode& node, const std::filesystem::path& current_path);
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<std::string> read_structure_file(const std::string& filepath);
void validate_structure(const std::vector<std::string>& 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 = '\\';
std::string sanitize_path(const std::string& path);
};
static const char DIRECTORY_MARKER = '/';
static const char TREE_PIPE = '';
static const char TREE_BRANCH = '';
static const char TREE_CORNER = '';
static const char TREE_DASH = '';
};