working generation script to generate proejct dirs with cli command create
Gitea Actions For Tree-Structurer / Explore-Gitea-Actions (push) Successful in 21s
Details
Gitea Actions For Tree-Structurer / Explore-Gitea-Actions (push) Successful in 21s
Details
This commit is contained in:
parent
2a2c13bddf
commit
3765449064
|
@ -67,6 +67,9 @@
|
||||||
"xmemory": "cpp",
|
"xmemory": "cpp",
|
||||||
"xstring": "cpp",
|
"xstring": "cpp",
|
||||||
"xtr1common": "cpp",
|
"xtr1common": "cpp",
|
||||||
"xutility": "cpp"
|
"xutility": "cpp",
|
||||||
|
"fstream": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"codecvt": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "prodir"
|
name = "prodir"
|
||||||
version = "0.0.6"
|
version = "0.0.7"
|
||||||
description = "A module for analyzing directory structures"
|
description = "A module for analyzing and creating directory structures"
|
||||||
scripts = {prodir = "prodir.__main__:main"}
|
scripts = {prodir = "prodir.__main__:main"}
|
||||||
dependencies = []
|
dependencies = []
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -20,7 +20,7 @@ tree_structurer_module = Extension(
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='prodir',
|
name='prodir',
|
||||||
version='0.0.6',
|
version='0.0.7',
|
||||||
description='A module for analyzing directory structures',
|
description='A module for analyzing directory structures',
|
||||||
ext_modules=[tree_structurer_module],
|
ext_modules=[tree_structurer_module],
|
||||||
packages=find_packages(where="src"),
|
packages=find_packages(where="src"),
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
from prodir._tree_structurer import (
|
from prodir._tree_structurer import (
|
||||||
create_tree_structurer,
|
create_tree_structurer,
|
||||||
get_structure,
|
get_structure,
|
||||||
create_structure_from_file,
|
create_structure_from_file
|
||||||
create_structure_from_string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'create_tree_structurer',
|
'create_tree_structurer',
|
||||||
'get_structure',
|
'get_structure',
|
||||||
'create_structure_from_file',
|
'create_structure_from_file'
|
||||||
'create_structure_from_string'
|
|
||||||
]
|
]
|
|
@ -4,18 +4,17 @@ import argparse
|
||||||
from prodir import (
|
from prodir import (
|
||||||
create_tree_structurer,
|
create_tree_structurer,
|
||||||
get_structure,
|
get_structure,
|
||||||
create_structure_from_file,
|
create_structure_from_file
|
||||||
create_structure_from_string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='Directory structure tool: Display and create directory structures'
|
description='Directory structure tool: Display and create directory structures'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create subparsers for different commands
|
# Create subparsers for different commands
|
||||||
subparsers = parser.add_subparsers(dest='command', help='Available commands')
|
subparsers = parser.add_subparsers(dest='command', help='Available commands')
|
||||||
|
|
||||||
# Display command
|
# Display command
|
||||||
display_parser = subparsers.add_parser('display', help='Display directory structure')
|
display_parser = subparsers.add_parser('display', help='Display directory structure')
|
||||||
display_parser.add_argument(
|
display_parser.add_argument(
|
||||||
|
@ -29,17 +28,13 @@ def main():
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='Show more detailed output'
|
help='Show more detailed output'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create command
|
# Create command
|
||||||
create_parser = subparsers.add_parser('create', help='Create directory structure')
|
create_parser = subparsers.add_parser('create', help='Create directory structure')
|
||||||
create_parser.add_argument(
|
create_parser.add_argument(
|
||||||
'-f', '--file',
|
'file',
|
||||||
help='File containing the directory structure'
|
help='File containing the directory structure'
|
||||||
)
|
)
|
||||||
create_parser.add_argument(
|
|
||||||
'-s', '--string',
|
|
||||||
help='String containing the directory structure'
|
|
||||||
)
|
|
||||||
create_parser.add_argument(
|
create_parser.add_argument(
|
||||||
'-o', '--output',
|
'-o', '--output',
|
||||||
default=os.getcwd(),
|
default=os.getcwd(),
|
||||||
|
@ -50,53 +45,40 @@ def main():
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='Show more detailed output'
|
help='Show more detailed output'
|
||||||
)
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# If no command is specified, default to display
|
# If no command is specified, default to display
|
||||||
if not args.command:
|
if not args.command:
|
||||||
args.command = 'display'
|
args.command = 'display'
|
||||||
args.path = os.getcwd()
|
args.path = os.getcwd()
|
||||||
args.verbose = False
|
args.verbose = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
create_tree_structurer()
|
create_tree_structurer()
|
||||||
|
|
||||||
if args.command == 'display':
|
if args.command == 'display':
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
print(f"Analyzing directory: {args.path}")
|
print(f"Analyzing directory: {args.path}")
|
||||||
|
|
||||||
structure = get_structure(args.path)
|
structure = get_structure(args.path)
|
||||||
for line in structure:
|
for line in structure:
|
||||||
print(line)
|
print(line)
|
||||||
|
|
||||||
elif args.command == 'create':
|
elif args.command == 'create':
|
||||||
if not args.file and not args.string:
|
|
||||||
parser.error("Either --file or --string must be specified")
|
|
||||||
|
|
||||||
if args.file and args.string:
|
|
||||||
parser.error("Cannot specify both --file and --string")
|
|
||||||
|
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
print(f"Creating directory structure in: {args.output}")
|
print(f"Creating directory structure in: {args.output}")
|
||||||
|
print(f"Using structure from file: {args.file}")
|
||||||
if args.file:
|
|
||||||
if args.verbose:
|
create_structure_from_file(args.file, args.output)
|
||||||
print(f"Using structure from file: {args.file}")
|
|
||||||
create_structure_from_file(args.file, args.output)
|
|
||||||
|
|
||||||
elif args.string:
|
|
||||||
if args.verbose:
|
|
||||||
print("Creating structure from provided string")
|
|
||||||
create_structure_from_string(args.string, args.output)
|
|
||||||
|
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
print("Structure created successfully")
|
print("Structure created successfully")
|
||||||
print("\nResulting structure:")
|
print("\nResulting structure:")
|
||||||
structure = get_structure(args.output)
|
structure = get_structure(args.output)
|
||||||
for line in structure:
|
for line in structure:
|
||||||
print(line)
|
print(line)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = str(e)
|
error_msg = str(e)
|
||||||
if "Directory does not exist" in error_msg:
|
if "Directory does not exist" in error_msg:
|
||||||
|
|
|
@ -66,28 +66,6 @@ static PyObject* create_structure_from_file(PyObject* self, PyObject* args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject* create_structure_from_string(PyObject* self, PyObject* args) {
|
|
||||||
const char* structure_str = nullptr;
|
|
||||||
const char* target_path = "."; // Default to current directory
|
|
||||||
|
|
||||||
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 {
|
|
||||||
g_tree_structurer->create_structure_from_string(structure_str, target_path);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
PyErr_SetString(TreeStructurerError, e.what());
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyMethodDef TreeStructurerMethods[] = {
|
static PyMethodDef TreeStructurerMethods[] = {
|
||||||
{"create_tree_structurer", create_tree_structurer, METH_NOARGS,
|
{"create_tree_structurer", create_tree_structurer, METH_NOARGS,
|
||||||
"Create a new TreeStructurer instance"},
|
"Create a new TreeStructurer instance"},
|
||||||
|
@ -95,8 +73,6 @@ static PyMethodDef TreeStructurerMethods[] = {
|
||||||
"Get the directory structure for the given path"},
|
"Get the directory structure for the given path"},
|
||||||
{"create_structure_from_file", create_structure_from_file, METH_VARARGS,
|
{"create_structure_from_file", create_structure_from_file, METH_VARARGS,
|
||||||
"Create directory structure from a file containing the structure description"},
|
"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}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@ std::string TreeStructurer::get_relative_path(const fs::path& path, const fs::pa
|
||||||
return rel.string();
|
return rel.string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<fs::path> TreeStructurer::get_filtered_paths(const fs::path& start) {
|
std::vector<fs::path> TreeStructurer::get_filtered_paths(const fs::path& start) {
|
||||||
std::vector<fs::path> paths;
|
std::vector<fs::path> paths;
|
||||||
fs::directory_options options = fs::directory_options::skip_permission_denied;
|
fs::directory_options options = fs::directory_options::skip_permission_denied;
|
||||||
|
@ -115,95 +116,93 @@ std::vector<fs::path> TreeStructurer::get_filtered_paths(const fs::path& start)
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Directory Structure Generation (tree printing)
|
// Directory Structure Generation (tree printing)
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
std::vector<std::string> TreeStructurer::get_directory_structure(const std::string& startpath) {
|
std::vector<std::string> TreeStructurer::get_directory_structure(const std::string& startpath) {
|
||||||
std::vector<std::string> lines;
|
std::vector<std::string> result;
|
||||||
fs::path start(startpath);
|
|
||||||
if (!fs::exists(start)) {
|
// Normalize the input path by removing ./ or .\ prefix if present
|
||||||
throw std::runtime_error("Path does not exist: " + startpath);
|
std::string normalized_path = startpath;
|
||||||
|
if (startpath.substr(0, 2) == ".\\" || startpath.substr(0, 2) == "./") {
|
||||||
|
normalized_path = startpath.substr(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path start = normalized_path.empty() ? fs::current_path() : fs::path(normalized_path);
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto paths = get_filtered_paths(start);
|
||||||
|
if (paths.empty()) {
|
||||||
|
throw std::runtime_error("No valid files or directories found in: " + start.string());
|
||||||
|
}
|
||||||
|
std::vector<bool> is_last_at_level(256, false);
|
||||||
|
|
||||||
|
for (size_t i = 1; i < paths.size(); ++i) {
|
||||||
|
const auto& path = paths[i];
|
||||||
|
std::string rel_path = get_relative_path(path, start);
|
||||||
|
|
||||||
|
int level = std::count(rel_path.begin(), rel_path.end(), fs::path::preferred_separator);
|
||||||
|
|
||||||
|
bool is_last = true;
|
||||||
|
for (size_t j = i + 1; j < paths.size(); ++j) {
|
||||||
|
std::string next_rel_path = get_relative_path(paths[j], start);
|
||||||
|
int next_level = std::count(next_rel_path.begin(), next_rel_path.end(), fs::path::preferred_separator);
|
||||||
|
if (next_level == level) {
|
||||||
|
is_last = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next_level < level) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is_last_at_level[level] = is_last;
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
for (int j = 0; j < level; ++j) {
|
||||||
|
if (j == level - 1) {
|
||||||
|
line += is_last ? "└── " : "├── ";
|
||||||
|
} else {
|
||||||
|
line += is_last_at_level[j] ? " " : "│ ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line += path.filename().string();
|
||||||
|
if (fs::is_directory(path)) {
|
||||||
|
line += "/";
|
||||||
|
}
|
||||||
|
result.push_back(line);
|
||||||
|
}
|
||||||
|
} catch (const fs::filesystem_error& e) {
|
||||||
|
throw std::runtime_error("Failed to access directory: " + std::string(e.what()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursive lambda that traverses the filesystem.
|
return result;
|
||||||
std::function<void(const fs::path&, const std::string&, bool)> traverse;
|
|
||||||
traverse = [&](const fs::path& path, const std::string& indent, bool isLast) {
|
|
||||||
std::string line;
|
|
||||||
if (indent.empty()) {
|
|
||||||
// For the root we simply output the name (appending a directory marker if needed)
|
|
||||||
line = path.filename().string();
|
|
||||||
} else {
|
|
||||||
// If not at the root, prepend a branch marker.
|
|
||||||
std::string branch = isLast ?
|
|
||||||
(std::string(1, static_cast<char>(TREE_CORNER)) + "── ") :
|
|
||||||
(std::string(1, static_cast<char>(TREE_BRANCH)) + "── ");
|
|
||||||
line = indent + branch + path.filename().string();
|
|
||||||
}
|
|
||||||
if (fs::is_directory(path)) {
|
|
||||||
line.push_back(static_cast<char>(DIRECTORY_MARKER));
|
|
||||||
}
|
|
||||||
lines.push_back(line);
|
|
||||||
|
|
||||||
if (fs::is_directory(path)) {
|
|
||||||
// Collect and sort the children.
|
|
||||||
std::vector<fs::path> children;
|
|
||||||
for (auto& entry : fs::directory_iterator(path)) {
|
|
||||||
std::string filename = entry.path().filename().string();
|
|
||||||
if (entry.is_directory() && should_ignore_dir(filename))
|
|
||||||
continue;
|
|
||||||
if (!entry.is_directory() && should_ignore_file(filename))
|
|
||||||
continue;
|
|
||||||
children.push_back(entry.path());
|
|
||||||
}
|
|
||||||
std::sort(children.begin(), children.end(), [](const fs::path& a,
|
|
||||||
const fs::path& b) {
|
|
||||||
return a.filename().string() < b.filename().string();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Recurse into children.
|
|
||||||
for (size_t i = 0; i < children.size(); i++) {
|
|
||||||
bool last = (i == children.size() - 1);
|
|
||||||
// New indent: if we are not at the root then extend the current indent.
|
|
||||||
std::string newIndent = indent;
|
|
||||||
if (!indent.empty()) {
|
|
||||||
newIndent += isLast ? " " : u8"│ ";
|
|
||||||
}
|
|
||||||
traverse(children[i], newIndent, last);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Start the traversal with an empty indent.
|
|
||||||
traverse(start, "", true);
|
|
||||||
return lines;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Structure Creation from a Tree-like File or String
|
// Structure Creation from a Tree-like File or String
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
void TreeStructurer::create_structure_from_file(const std::string& filepath,
|
void TreeStructurer::create_structure_from_file(const std::string& filepath,
|
||||||
const std::string& target_path) {
|
const std::string& target_path) {
|
||||||
std::vector<std::string> lines = read_structure_file(filepath);
|
std::vector<std::string> lines = read_structure_file(filepath);
|
||||||
validate_structure(lines);
|
validate_structure(lines);
|
||||||
TreeNode root = build_tree_from_lines(lines);
|
TreeNode root = build_tree_from_lines(lines);
|
||||||
create_node(root, fs::path(target_path));
|
|
||||||
}
|
fs::path target(target_path);
|
||||||
|
|
||||||
void TreeStructurer::create_structure_from_string(const std::string& structure,
|
// Check if the root directory name matches the target directory name
|
||||||
const std::string& target_path) {
|
if (root.name == target.filename().string()) {
|
||||||
std::vector<std::string> lines;
|
// If names match, create the children directly in the target directory
|
||||||
std::istringstream iss(structure);
|
for (const auto& child : root.children) {
|
||||||
std::string line;
|
create_node(child, target);
|
||||||
while (std::getline(iss, line)) {
|
|
||||||
if (!line.empty()) {
|
|
||||||
lines.push_back(line);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// If names don't match, create the full structure including root
|
||||||
|
create_node(root, target);
|
||||||
}
|
}
|
||||||
validate_structure(lines);
|
|
||||||
TreeNode root = build_tree_from_lines(lines);
|
|
||||||
create_node(root, fs::path(target_path));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -217,55 +216,115 @@ std::string TreeStructurer::create_indent(size_t level) {
|
||||||
|
|
||||||
// Determines the "indent level" of a line by scanning it in 4‑character groups.
|
// Determines the "indent level" of a line by scanning it in 4‑character groups.
|
||||||
// Recognizes either a blank indent (" " or "│ ") or a branch marker ("├── " or "└── ").
|
// Recognizes either a blank indent (" " or "│ ") or a branch marker ("├── " or "└── ").
|
||||||
|
|
||||||
size_t TreeStructurer::get_indent_level(const std::string& line) {
|
size_t TreeStructurer::get_indent_level(const std::string& line) {
|
||||||
size_t indent = 0;
|
size_t indent = 0;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
while (pos + 4 <= line.size()) {
|
|
||||||
std::string group = line.substr(pos, 4);
|
while (pos < line.length()) {
|
||||||
if (group == " " || group == u8"│ ") {
|
if (pos >= line.length()) break;
|
||||||
|
|
||||||
|
// Check for basic space indent (4 spaces)
|
||||||
|
if (pos + 3 < line.length() && line.substr(pos, 4) == " ") {
|
||||||
indent++;
|
indent++;
|
||||||
pos += 4;
|
pos += 4;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (group == u8"├── " || group == u8"└── ") {
|
|
||||||
|
// Check for │ (vertical line) followed by 3 spaces
|
||||||
|
// Bytes: E2 94 82 20 20 20
|
||||||
|
if (pos + 5 < line.length() &&
|
||||||
|
static_cast<unsigned char>(line[pos]) == 0xE2 &&
|
||||||
|
static_cast<unsigned char>(line[pos + 1]) == 0x94 &&
|
||||||
|
static_cast<unsigned char>(line[pos + 2]) == 0x82 &&
|
||||||
|
line[pos + 3] == ' ' &&
|
||||||
|
line[pos + 4] == ' ' &&
|
||||||
|
line[pos + 5] == ' ') {
|
||||||
indent++;
|
indent++;
|
||||||
break;
|
pos += 6;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for ├── or └── (branch or corner followed by dashes and space)
|
||||||
|
// ├ = E2 94 9C
|
||||||
|
// └ = E2 94 94
|
||||||
|
// ─ = E2 94 80
|
||||||
|
if (pos + 8 < line.length() &&
|
||||||
|
static_cast<unsigned char>(line[pos]) == 0xE2 &&
|
||||||
|
static_cast<unsigned char>(line[pos + 1]) == 0x94 &&
|
||||||
|
(static_cast<unsigned char>(line[pos + 2]) == 0x9C || // ├
|
||||||
|
static_cast<unsigned char>(line[pos + 2]) == 0x94) && // └
|
||||||
|
static_cast<unsigned char>(line[pos + 3]) == 0xE2 &&
|
||||||
|
static_cast<unsigned char>(line[pos + 4]) == 0x94 &&
|
||||||
|
static_cast<unsigned char>(line[pos + 5]) == 0x80 && // ─
|
||||||
|
static_cast<unsigned char>(line[pos + 6]) == 0xE2 &&
|
||||||
|
static_cast<unsigned char>(line[pos + 7]) == 0x94 &&
|
||||||
|
static_cast<unsigned char>(line[pos + 8]) == 0x80 && // ─
|
||||||
|
(pos + 9 >= line.length() || line[pos + 9] == ' ')) {
|
||||||
|
indent++;
|
||||||
|
break; // We've found our indent marker, stop here
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here without finding a valid indent pattern, we're done
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return indent;
|
return indent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses a single line of the structure (after knowing its indent level) and returns a TreeNode.
|
// Parses a single line of the structure (after knowing its indent level) and returns a TreeNode.
|
||||||
// The function "consumes" indent groups until the branch marker.
|
// The function "consumes" indent groups until the branch marker.
|
||||||
TreeStructurer::TreeNode TreeStructurer::parse_structure_line(const std::string& line,
|
TreeStructurer::TreeNode TreeStructurer::parse_structure_line(const std::string& line, size_t indent_level) {
|
||||||
size_t indent_level) {
|
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
size_t groups = 0;
|
size_t current_indent = 0;
|
||||||
while (pos + 4 <= line.size() && groups < indent_level) {
|
|
||||||
std::string group = line.substr(pos, 4);
|
// Skip through indentation patterns
|
||||||
if (group == " " || group == u8"│ ") {
|
while (current_indent < indent_level && pos < line.length()) {
|
||||||
|
// Check for basic space indent
|
||||||
|
if (pos + 3 < line.length() && line.substr(pos, 4) == " ") {
|
||||||
pos += 4;
|
pos += 4;
|
||||||
groups++;
|
current_indent++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (group == u8"├── " || group == u8"└── ") {
|
|
||||||
pos += 4;
|
// Check for │ followed by spaces
|
||||||
groups++;
|
if (pos + 5 < line.length() &&
|
||||||
|
static_cast<unsigned char>(line[pos]) == 0xE2 &&
|
||||||
|
static_cast<unsigned char>(line[pos + 1]) == 0x94 &&
|
||||||
|
static_cast<unsigned char>(line[pos + 2]) == 0x82 &&
|
||||||
|
line[pos + 3] == ' ' &&
|
||||||
|
line[pos + 4] == ' ' &&
|
||||||
|
line[pos + 5] == ' ') {
|
||||||
|
pos += 6;
|
||||||
|
current_indent++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for ├── or └── pattern
|
||||||
|
if (pos + 9 < line.length() &&
|
||||||
|
static_cast<unsigned char>(line[pos]) == 0xE2 &&
|
||||||
|
static_cast<unsigned char>(line[pos + 1]) == 0x94 &&
|
||||||
|
(static_cast<unsigned char>(line[pos + 2]) == 0x9C ||
|
||||||
|
static_cast<unsigned char>(line[pos + 2]) == 0x94)) {
|
||||||
|
pos += 10; // Skip the entire pattern including space
|
||||||
|
current_indent++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract the name (everything after the indentation)
|
||||||
std::string name = line.substr(pos);
|
std::string name = line.substr(pos);
|
||||||
name = sanitize_path(name);
|
name = sanitize_path(name);
|
||||||
|
|
||||||
bool is_file = true;
|
bool is_file = true;
|
||||||
if (!name.empty() && name.back() == static_cast<char>(DIRECTORY_MARKER)) {
|
if (!name.empty() && name.back() == '/') {
|
||||||
is_file = false;
|
is_file = false;
|
||||||
name.pop_back(); // Remove the directory marker.
|
name.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
return TreeNode{name, is_file, {}};
|
return TreeNode{name, is_file, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builds a tree (with TreeNode nodes) from the vector of structure lines.
|
// Builds a tree (with TreeNode nodes) from the vector of structure lines.
|
||||||
// The first line is assumed to be the root.
|
// The first line is assumed to be the root.
|
||||||
TreeStructurer::TreeNode TreeStructurer::build_tree_from_lines(const std::vector<std::string>& lines) {
|
TreeStructurer::TreeNode TreeStructurer::build_tree_from_lines(const std::vector<std::string>& lines) {
|
||||||
|
@ -350,31 +409,42 @@ void TreeStructurer::create_file(const fs::path& path) {
|
||||||
// Reads a structure file into a vector of non-empty lines.
|
// Reads a structure file into a vector of non-empty lines.
|
||||||
std::vector<std::string> TreeStructurer::read_structure_file(const std::string& filepath) {
|
std::vector<std::string> TreeStructurer::read_structure_file(const std::string& filepath) {
|
||||||
std::vector<std::string> lines;
|
std::vector<std::string> lines;
|
||||||
std::ifstream file(filepath);
|
// Open file in binary mode to avoid Windows CRLF conversion
|
||||||
|
std::ifstream file(filepath, std::ios::binary);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
throw std::runtime_error("Failed to open file: " + filepath);
|
throw std::runtime_error("Failed to open file: " + filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(file, line)) {
|
while (std::getline(file, line)) {
|
||||||
|
// Remove carriage return if present (Windows files)
|
||||||
|
if (!line.empty() && line.back() == '\r') {
|
||||||
|
line.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
if (!line.empty()) {
|
if (!line.empty()) {
|
||||||
lines.push_back(line);
|
lines.push_back(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks the structure for obvious mistakes (e.g. a jump in indentation).
|
// Checks the structure for obvious mistakes (e.g. a jump in indentation).
|
||||||
void TreeStructurer::validate_structure(const std::vector<std::string>& lines) {
|
void TreeStructurer::validate_structure(const std::vector<std::string>& lines) {
|
||||||
if (lines.empty()) {
|
if (lines.empty()) {
|
||||||
throw std::runtime_error("Empty structure provided");
|
throw std::runtime_error("Empty structure provided");
|
||||||
}
|
}
|
||||||
size_t prev_indent = 0;
|
size_t prev_indent = 0;
|
||||||
for (const auto& line : lines) {
|
for (size_t i = 0; i < lines.size(); i++) {
|
||||||
|
const auto& line = lines[i];
|
||||||
if (line.empty()) continue;
|
if (line.empty()) continue;
|
||||||
size_t indent = get_indent_level(line);
|
|
||||||
std::cout << "Line: \"" << line << "\" Indent level: " << indent << std::endl;
|
size_t indent = get_indent_level(line);
|
||||||
if (indent > prev_indent + 1) {
|
if (indent > prev_indent + 1) {
|
||||||
throw std::runtime_error("Invalid indentation structure in the file");
|
throw std::runtime_error(
|
||||||
|
"Invalid indentation jump at line " + std::to_string(i + 1) +
|
||||||
|
": from level " + std::to_string(prev_indent) +
|
||||||
|
" to " + std::to_string(indent)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
prev_indent = indent;
|
prev_indent = indent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,7 @@ public:
|
||||||
TreeStructurer() = default;
|
TreeStructurer() = default;
|
||||||
|
|
||||||
std::vector<std::string> get_directory_structure(const std::string& startpath = std::filesystem::current_path().string());
|
std::vector<std::string> 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_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:
|
private:
|
||||||
struct TreeNode {
|
struct TreeNode {
|
||||||
|
|
Loading…
Reference in New Issue