-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtree_setup.py
More file actions
executable file
·139 lines (127 loc) · 6.32 KB
/
Copy pathtree_setup.py
File metadata and controls
executable file
·139 lines (127 loc) · 6.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/usr/bin/env python3
"""
Create directories and files from a tree-like text representation.
Usage: ./tree_setup.py [tree_file] # read from file or stdin
Example: ./tree_setup.py <<EOF
bedepacko/
├── bede.sh
├── bede-engine/
│ ├── Cargo.toml
│ ├── src/
│ │ ├── main.rs
│ │ ├── downloader.rs
│ │ ├── resolver.rs
│ │ ├── manifest.rs
│ │ └── lockfile.rs
│ └── target/
EOF
"""
import sys
import os
import re
from pathlib import Path
def clean_name(name: str) -> str:
"""Remove trailing comments like ' (your existing script)' and strip spaces."""
# Remove anything starting with ' (' or ' ('
name = re.sub(r'\s*\([^)]*\)', '', name)
# Remove trailing spaces and slashes (we'll re-add slashes for dirs)
return name.strip().rstrip('/')
def parse_tree(lines):
"""
Parse lines from `tree` output (like `tree --charset=ascii` but works with unicode too).
Returns a list of (path_string, is_dir) where path_string is relative to the root.
"""
entries = []
# We'll reconstruct the full path by tracking the stack of parents.
# Each line has indentation represented by '│ ' and entry markers like '├── ' or '└── '.
# Simpler approach: use a stack of (indent_level, current_prefix)
stack = []
for raw_line in lines:
line = raw_line.rstrip('\n')
if not line.strip():
continue
# Count the number of '│ ' (or space/pipe combos) to get indent level
# Also handle plain spaces if someone uses a different tree output.
# We'll look for tree connector characters: '├── ', '└── ', '│ '
# Remove leading spaces first, but we need indent depth.
# Better: find position of first non-space, non-│ non-├ non-└ character? Or use regex.
# Let's use a simple approach: count the number of '│' and spaces; each level is either "│ " or " ".
# Actually, we can use the known pattern: each line starts with some number of "│ " (or spaces)
# followed by "├── " or "└── " and then the name.
# We'll measure indent by counting occurrences of "│" or spaces before the first '├', '└', or '─'?
# Simpler: split by the first '├' or '└' (the connector).
# We'll instead use a method that extracts the indent prefix and the entry part.
# Python example from many tree parsers: https://stackoverflow.com/questions/46993923/parse-tree-format
# Search for the first occurrence of '├── ' or '└── '
match = re.search(r'(├── |└── )', line)
if not match:
# Maybe it's the root line (e.g., "bedepacko/") without any prefix
# root line should contain no '├' or '└' and no leading spaces.
if line.endswith('/') or (not line.startswith((' ', '│', '├', '└'))):
# Assume it's the root directory
name = clean_name(line)
entries.append((name, True)) # root is a directory
stack = [(0, name)] # (indent_level, current_path)
continue
connector = match.group(1)
start_idx = match.start()
prefix = line[:start_idx] # contains the indentation characters (│, spaces)
name_part = line[match.end():]
name = clean_name(name_part)
# Determine if it's a directory: ends with '/'
is_dir = name.endswith('/') or (name_part.strip() and name_part.strip().endswith('/'))
# Remove trailing slash for path construction
name_clean = name.rstrip('/')
# Calculate indent level: each level corresponds to a "│ " or " " (4 spaces)
# We'll count the number of occurrences of '│' (pipe) or groups of spaces? Actually easier:
# Count number of '│' in the prefix (since each level adds one '│' either directly or after a space)
# But also spaces count as continuation. Let's use a reliable method: number of '├'? No.
# Instead, compute the depth by the number of '│' plus maybe the root.
# Typical tree: level 0: no pipe. level 1: "├── " preceded by ""? Actually root line has no prefix.
# For child: prefix could be "│ " (one pipe + 3 spaces). Grandchild: "│ │ " etc.
# So number of "│" characters in prefix indicates the depth.
depth = prefix.count('│')
# Also handle spaces: some trees use spaces instead of '│' – if no '│', count groups of 4 spaces.
if depth == 0 and ' ' in prefix:
depth = prefix.count(' ')
# However, the connector itself defines the relation to the parent.
# We'll reconstruct path by walking up the stack.
# Stack holds tuples (depth, current_path_string)
# Pop until we find the parent for this depth.
while stack and stack[-1][0] >= depth:
stack.pop()
# Now the top of stack is the parent (depth = depth-1)
if not stack:
# Shouldn't happen for valid tree, but fallback: use root from earlier
continue
parent_path = stack[-1][1]
full_path = os.path.join(parent_path, name_clean)
entries.append((full_path, is_dir))
# Push this node onto stack for its children
stack.append((depth, full_path))
return entries
def create_from_entries(entries, base_dir='.'):
"""Create directories and empty files from the parsed entries."""
for path, is_dir in entries:
full = Path(base_dir) / path
if is_dir:
full.mkdir(parents=True, exist_ok=True)
print(f"[DIR] {full}")
else:
# Ensure parent directory exists
full.parent.mkdir(parents=True, exist_ok=True)
# Create empty file (won't overwrite existing)
full.touch(exist_ok=True)
print(f"[FILE] {full}")
def main():
if len(sys.argv) > 1:
with open(sys.argv[1], 'r', encoding='utf-8') as f:
lines = f.readlines()
else:
lines = sys.stdin.readlines()
entries = parse_tree(lines)
# Optional: filter out 'target/' if you don't want it created (usually build output)
# entries = [(p, d) for p, d in entries if not p.startswith('target')]
create_from_entries(entries)
if __name__ == '__main__':
main()