updated getting dir
Gitea Actions For Tree-Structurer / Explore-Gitea-Actions (push) Successful in 23s
Details
Gitea Actions For Tree-Structurer / Explore-Gitea-Actions (push) Successful in 23s
Details
This commit is contained in:
parent
16f0e89e91
commit
0d01f188e7
|
@ -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 Unicode‑aware (token‑based) 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 4‑character 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 token‐based 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = '─';
|
||||
};
|
Loading…
Reference in New Issue