tree2project 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ from .version import version
2
+
3
+ all = ["**version**"]
tree2project/cli.py ADDED
@@ -0,0 +1,89 @@
1
+ import argparse
2
+
3
+ from tree2project.commands.create import (
4
+ run as create_run
5
+ )
6
+ from tree2project.commands.export import (
7
+ run as export_run
8
+ )
9
+ from tree2project.commands.validate import (
10
+ run as validate_run
11
+ )
12
+
13
+
14
+ def main():
15
+ parser = argparse.ArgumentParser(
16
+ prog="tree2project"
17
+ )
18
+
19
+ subparsers = parser.add_subparsers(
20
+ dest="command"
21
+ )
22
+
23
+ create_parser = subparsers.add_parser(
24
+ "create",
25
+ help="Create structure"
26
+ )
27
+
28
+ create_parser.add_argument(
29
+ "structure_file"
30
+ )
31
+
32
+ create_parser.add_argument(
33
+ "--target"
34
+ )
35
+
36
+ create_parser.add_argument(
37
+ "--dry-run",
38
+ action="store_true"
39
+ )
40
+
41
+ export_parser = subparsers.add_parser(
42
+ "export",
43
+ help="Export structure"
44
+ )
45
+
46
+ export_parser.add_argument(
47
+ "directory"
48
+ )
49
+
50
+ export_parser.add_argument(
51
+ "--output"
52
+ )
53
+
54
+ validate_parser = subparsers.add_parser(
55
+ "validate",
56
+ help="Validate structure"
57
+ )
58
+
59
+ validate_parser.add_argument(
60
+ "structure_file"
61
+ )
62
+
63
+ validate_parser.add_argument(
64
+ "--target",
65
+ required=True
66
+ )
67
+
68
+ args = parser.parse_args()
69
+
70
+ if args.command == "create":
71
+ raise SystemExit(
72
+ create_run(args)
73
+ )
74
+
75
+ elif args.command == "export":
76
+ raise SystemExit(
77
+ export_run(args)
78
+ )
79
+
80
+ elif args.command == "validate":
81
+ raise SystemExit(
82
+ validate_run(args)
83
+ )
84
+
85
+ parser.print_help()
86
+
87
+
88
+ if __name__ == "__main__":
89
+ main()
@@ -0,0 +1,89 @@
1
+ from pathlib import Path
2
+
3
+ from rich.console import Console
4
+ from rich.table import Table
5
+
6
+ from tree2project.parser import parse_structure
7
+ from tree2project.creator import create_structure
8
+
9
+ console = Console()
10
+
11
+
12
+ def run(args):
13
+ structure_file = Path(args.structure_file)
14
+
15
+ if not structure_file.exists():
16
+ console.print(
17
+ f"[red]File not found:[/red] {structure_file}"
18
+ )
19
+ return 1
20
+
21
+ with open(
22
+ structure_file,
23
+ "r",
24
+ encoding="utf-8"
25
+ ) as f:
26
+ lines = f.readlines()
27
+
28
+ paths = parse_structure(lines)
29
+
30
+ target = (
31
+ Path(args.target).expanduser().resolve()
32
+ if args.target
33
+ else Path(
34
+ console.input(
35
+ "Target directory: "
36
+ )
37
+ ).expanduser().resolve()
38
+ )
39
+
40
+ table = Table(title="Structure Preview")
41
+
42
+ table.add_column("Type")
43
+ table.add_column("Path")
44
+
45
+ for path, is_dir in paths:
46
+ table.add_row(
47
+ "DIR" if is_dir else "FILE",
48
+ str(target / path)
49
+ )
50
+
51
+ console.print(table)
52
+
53
+ if args.dry_run:
54
+ console.print(
55
+ "[yellow]Dry run complete.[/yellow]"
56
+ )
57
+ return 0
58
+
59
+ confirm = console.input(
60
+ "Create structure? [y/N]: "
61
+ )
62
+
63
+ if confirm.lower() != "y":
64
+ console.print(
65
+ "[yellow]Cancelled[/yellow]"
66
+ )
67
+ return 0
68
+
69
+ result = create_structure(
70
+ paths,
71
+ target
72
+ )
73
+
74
+ console.print(
75
+ f"[green]Directories:[/green] "
76
+ f"{len(result['created_dirs'])}"
77
+ )
78
+
79
+ console.print(
80
+ f"[green]Files:[/green] "
81
+ f"{len(result['created_files'])}"
82
+ )
83
+
84
+ console.print(
85
+ f"[yellow]Skipped:[/yellow] "
86
+ f"{len(result['skipped_files'])}"
87
+ )
88
+
89
+ return 0
@@ -0,0 +1,35 @@
1
+ from pathlib import Path
2
+
3
+ from rich.console import Console
4
+
5
+ from tree2project.exporter import export_structure
6
+
7
+ console = Console()
8
+
9
+
10
+ def run(args):
11
+ root = Path(args.directory)
12
+
13
+ if not root.exists():
14
+ console.print(
15
+ f"[red]Directory not found:[/red] {root}"
16
+ )
17
+ return 1
18
+
19
+ structure = export_structure(root)
20
+
21
+ if args.output:
22
+ with open(
23
+ args.output,
24
+ "w",
25
+ encoding="utf-8"
26
+ ) as f:
27
+ f.write(structure)
28
+
29
+ console.print(
30
+ f"[green]Saved:[/green] {args.output}"
31
+ )
32
+ else:
33
+ console.print(structure)
34
+
35
+ return 0
@@ -0,0 +1,43 @@
1
+ from pathlib import Path
2
+
3
+ from rich.console import Console
4
+
5
+ from tree2project.parser import parse_structure
6
+ from tree2project.validator import (
7
+ validate_structure
8
+ )
9
+
10
+ console = Console()
11
+
12
+
13
+ def run(args):
14
+ structure_file = Path(args.structure_file)
15
+
16
+ with open(
17
+ structure_file,
18
+ "r",
19
+ encoding="utf-8"
20
+ ) as f:
21
+ lines = f.readlines()
22
+
23
+ paths = parse_structure(lines)
24
+
25
+ result = validate_structure(
26
+ paths,
27
+ args.target
28
+ )
29
+
30
+ if result["valid"]:
31
+ console.print(
32
+ "[green]Structure valid[/green]"
33
+ )
34
+ return 0
35
+
36
+ console.print(
37
+ "[red]Missing paths:[/red]"
38
+ )
39
+
40
+ for item in result["missing"]:
41
+ console.print(item)
42
+
43
+ return 1
@@ -0,0 +1,41 @@
1
+ from pathlib import Path
2
+
3
+ def create_structure(paths, target_dir, dry_run=False):
4
+ target_dir = Path(target_dir)
5
+
6
+
7
+ created_dirs = []
8
+ created_files = []
9
+ skipped_files = []
10
+
11
+ for relative_path, is_dir in paths:
12
+ full_path = target_dir / relative_path
13
+
14
+ if dry_run:
15
+ continue
16
+
17
+ if is_dir:
18
+ full_path.mkdir(
19
+ parents=True,
20
+ exist_ok=True
21
+ )
22
+ created_dirs.append(str(full_path))
23
+ else:
24
+ full_path.parent.mkdir(
25
+ parents=True,
26
+ exist_ok=True
27
+ )
28
+
29
+ if full_path.exists():
30
+ skipped_files.append(str(full_path))
31
+ continue
32
+
33
+ full_path.touch()
34
+ created_files.append(str(full_path))
35
+
36
+ return {
37
+ "created_dirs": created_dirs,
38
+ "created_files": created_files,
39
+ "skipped_files": skipped_files,
40
+ }
41
+
@@ -0,0 +1,42 @@
1
+
2
+ from pathlib import Path
3
+
4
+
5
+ def export_structure(root_path):
6
+ root = Path(root_path)
7
+
8
+ if not root.exists():
9
+ raise FileNotFoundError(root)
10
+
11
+ lines = [f"{root.name}/"]
12
+
13
+ def walk(directory, prefix=""):
14
+ items = sorted(
15
+ directory.iterdir(),
16
+ key=lambda p: (p.is_file(), p.name.lower())
17
+ )
18
+
19
+ for idx, item in enumerate(items):
20
+ last = idx == len(items) - 1
21
+
22
+ connector = "└── " if last else "├── "
23
+
24
+ if item.is_dir():
25
+ lines.append(
26
+ f"{prefix}{connector}{item.name}/"
27
+ )
28
+
29
+ extension = " " if last else "│ "
30
+
31
+ walk(
32
+ item,
33
+ prefix + extension
34
+ )
35
+ else:
36
+ lines.append(
37
+ f"{prefix}{connector}{item.name}"
38
+ )
39
+
40
+ walk(root)
41
+
42
+ return "\n".join(lines)
tree2project/parser.py ADDED
@@ -0,0 +1,64 @@
1
+ from pathlib import Path
2
+ import re
3
+
4
+ def parse_structure(lines):
5
+ paths = []
6
+ stack = []
7
+
8
+
9
+ for raw in lines:
10
+ line = raw.rstrip("\n")
11
+
12
+ if not line.strip():
13
+ continue
14
+
15
+ stripped = line.strip()
16
+
17
+ if re.fullmatch(r"[│\s]+", stripped):
18
+ continue
19
+
20
+ if not any(token in line for token in ("├──", "└──")):
21
+ name = stripped.rstrip("/")
22
+
23
+ if stripped.endswith("/"):
24
+ stack = [name]
25
+ paths.append((Path(name), True))
26
+ else:
27
+ paths.append((Path(name), False))
28
+
29
+ continue
30
+
31
+ prefix = (
32
+ line.split("├──")[0]
33
+ if "├──" in line
34
+ else line.split("└──")[0]
35
+ )
36
+
37
+ depth = len(prefix) // 4
38
+
39
+ name = re.sub(
40
+ r"^[ │]*(├──|└──)\s*",
41
+ "",
42
+ line,
43
+ ).strip()
44
+
45
+ is_dir = name.endswith("/")
46
+ name = name.rstrip("/")
47
+
48
+ while len(stack) > depth + 1:
49
+ stack.pop()
50
+
51
+ parent = Path(*stack) if stack else Path()
52
+ current = parent / name
53
+
54
+ paths.append((current, is_dir))
55
+
56
+ if is_dir:
57
+ if len(stack) == depth + 1:
58
+ stack.append(name)
59
+ else:
60
+ stack = stack[: depth + 1]
61
+ stack.append(name)
62
+
63
+ return paths
64
+
@@ -0,0 +1,25 @@
1
+ from pathlib import Path
2
+
3
+
4
+ def validate_structure(paths, target_dir):
5
+ target_dir = Path(target_dir)
6
+
7
+ missing = []
8
+
9
+ for relative_path, is_dir in paths:
10
+ full_path = target_dir / relative_path
11
+
12
+ if not full_path.exists():
13
+ missing.append(str(full_path))
14
+ continue
15
+
16
+ if is_dir and not full_path.is_dir():
17
+ missing.append(str(full_path))
18
+
19
+ if not is_dir and not full_path.is_file():
20
+ missing.append(str(full_path))
21
+
22
+ return {
23
+ "valid": len(missing) == 0,
24
+ "missing": missing,
25
+ }
@@ -0,0 +1 @@
1
+ version = "0.1.0"
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: tree2project
3
+ Version: 0.1.0
4
+ Summary: Create project folder structures from tree-format files
5
+ Author-email: Dinesh kumar ummaneni <dineshkumarummaneni@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/yourname/tree2project
8
+ Project-URL: Repository, https://github.com/yourname/tree2project
9
+ Keywords: cli,filesystem,project-generator,scaffold,tree
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: rich>=13.7.0
23
+ Dynamic: license-file
@@ -0,0 +1,16 @@
1
+ tree2project/__init__.py,sha256=WHi6FocJKDao1jtIW45ZRSEDjnV8DYKGT-gDDksMLqs,52
2
+ tree2project/cli.py,sha256=8evTlExsDctOvFI2xb8Xeu8VF7mENpSb2hCDvY4FwPY,1561
3
+ tree2project/creator.py,sha256=CVCzLHKviY-mSSnU2R0EwuIiP_ZA53LWHUrVtzuZceE,948
4
+ tree2project/exporter.py,sha256=C6GutxW9wdbhnW8Zvie6T8IoIC8aWN7_MXtoPEhub18,956
5
+ tree2project/parser.py,sha256=QMCXSb75iMkx9nrnFOdeFUzmyOlGtt1xtiRzT6XEAcM,1404
6
+ tree2project/validator.py,sha256=7ZaK5Vwn9CQQ_doQQQLleTo9kfL4v0skRqOQkL6HzFM,579
7
+ tree2project/version.py,sha256=aOHawL1zuHMfBWKXqwUkXcW96oXLNCY-CXdHDqkz4g4,18
8
+ tree2project/commands/create.py,sha256=OzupKEGSxMMvMiIVbkOFLwObmoUDINVa9YlGwQJwFKI,1779
9
+ tree2project/commands/export.py,sha256=d1AAqTM9fGzTUXrUv_KGbN5gCrX7aQQsSqGxySFiWEw,651
10
+ tree2project/commands/validate.py,sha256=6E7wGiVaWDyuXwd_g_ODql39ntHxHam53biXghCSchY,745
11
+ tree2project-0.1.0.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ tree2project-0.1.0.dist-info/METADATA,sha256=Q14sNOSzv221WpYOnkTVqhzHy8KCCx00w0SqxNAq4hE,948
13
+ tree2project-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
14
+ tree2project-0.1.0.dist-info/entry_points.txt,sha256=GNc6K6jDygrD1IDN2-67ua11P5BCRqnPLrQHHE7c7uI,55
15
+ tree2project-0.1.0.dist-info/top_level.txt,sha256=suqoBy3dNCYhGMBrgUmtazE2ztGTP515g1dy7h3Ebpc,13
16
+ tree2project-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ tree2project = tree2project.cli:main
File without changes
@@ -0,0 +1 @@
1
+ tree2project