diff --git a/README.md b/README.md index 7e53f02..4b2ce24 100644 --- a/README.md +++ b/README.md @@ -9,57 +9,114 @@

-A simple and powerful CLI tool to create project folder and file structures from a Markdown tree. +A simple and powerful CLI tool to create project folder and file structures from **Markdown** and **YAML** tree definitions. -Stop creating files and folders manually. Define your project's skeleton in a readable Markdown file and let `nirman` build it for you in seconds. +Stop creating files and folders manually. Define your project’s skeleton in a readable Markdown or YAML file, and let `nirman` build it for you in seconds. ## Key Features -- **Intuitive Input:** Uses a visual, tree-style Markdown format. -- **Safe by Default:** Includes a `--dry-run` mode to preview changes. -- **Flexible:** Supports overwriting files with the `--force` flag. -- **Simple & Lightweight:** No external dependencies. +* **Two Input Formats:** + + * Markdown (`.md`, `.markdown`) + * YAML (`.yml`, `.yaml`) +* **Readable Tree Syntax:** Write clean collapsible structures. +* **Safe Execution:** Preview actions with `--dry-run`. +* **Flexible:** Overwrite files using `--force`. +* **Lightweight & Fast:** Uses simple tree-based parsing. +* **Cross-platform:** Works on Linux, macOS, and Windows. + +--- ## Installation -You can install `Nirman-cli` directly from PyPI: +Install from PyPI: ```bash pip install Nirman-cli ``` -## Usage +--- -1. Create a Markdown file (e.g., `structure.md`) defining your desired project layout: +# Usage - ```markdown - my-python-app/ - ├── src/ - │ ├── __init__.py - │ └── main.py - ├── tests/ - │ └── test_main.py - ├── .gitignore - └── README.md - ``` +## 1) Markdown Example + +Create a file `structure.md`: -2. Run the `nirman` command from your terminal: +```markdown +my-python-app/ +├── src/ +│ ├── __init__.py +│ └── main.py +├── tests/ +│ └── test_main.py +└── README.md +``` - ```bash - nirman structure.md - ``` +Build the structure: - This will create the `my-python-app/` directory and all its contents in your current location. +```bash +nirman structure.md +``` -### Command-Line Options +--- + +## 2) YAML Example + +Nirman also supports YAML. +**Rule:** Individual files must be listed under a `files:` key. + +Example (`structure.yml`): + +```yaml +project: + src: + files: + - main.py + - utils.py + services: + api: + files: + - handler.py + - routes.py + files: + - README.md + - .gitignore +``` + +Build it: + +```bash +nirman structure.yml +``` + +This produces: + +``` +output_folder/ +└── project/ + ├── src/ + │ ├── main.py + │ └── utils.py + ├── services/ + │ └── api/ + │ ├── handler.py + │ └── routes.py + ├── README.md + └── .gitignore +``` + +--- + +# Command-Line Options ``` usage: nirman [-h] [-o OUTPUT] [--dry-run] [-f] input_file -Build a project structure from a Markdown tree file. +Build a project structure from a Markdown (.md) or YAML (.yml/.yaml) file. positional arguments: - input_file Path to the Markdown file containing the project structure (must have a .md or .markdown extension). + input_file Path to the structure file. options: -h, --help show this help message and exit @@ -69,6 +126,36 @@ options: -f, --force Overwrite existing files if they are encountered. ``` +--- + +# YAML Rules (Important) + +Your YAML structure must follow these rules: + +1. **Every folder is a dictionary key.** +2. **All direct files inside a folder must be placed under:** + + ```yaml + files: + - file1.txt + - file2.py + ``` +3. Nested folders must be dictionaries. +4. Lists may contain: + + * file names (strings) + * dictionaries for nested folders + +This rule is reflected in the updated parser: + +```python +# Individual files must be under "files:" +if key == "files": + ... +``` + +--- + ## License This project is licensed under the MIT License. See the [LICENSE](https://github.com/Hemanth0411/Nirman-cli/blob/main/LICENSE) file for details. diff --git a/structure.yaml b/structure.yaml index ba1827d..4e427a4 100644 --- a/structure.yaml +++ b/structure.yaml @@ -1,7 +1,16 @@ project: src: - - main.py - - utils: - - helper.py - tests: - - test_main.py + files: + - main.py + - utils.py + services: + api: + files: + - handler.py + - routes.py + files: + - config.yaml + - requirements.txt + files: + - README.md + - .gitignore \ No newline at end of file diff --git a/tests/test_cli_yaml_basic.py b/tests/test_cli_yaml_basic.py new file mode 100644 index 0000000..bedb7bc --- /dev/null +++ b/tests/test_cli_yaml_basic.py @@ -0,0 +1,28 @@ +import sys +from pathlib import Path + + +def test_cli_yaml_basic_generation(tmp_path, monkeypatch): + yaml_data = """ +project: + files: + - a.py + - b.txt +""" + + input_file = tmp_path / "structure.yml" + input_file.write_text(yaml_data) + + output_dir = tmp_path / "out" + + monkeypatch.setattr( + sys, "argv", + ["nirman", str(input_file), "-o", str(output_dir)] + ) + + from nirman.cli import main + main() + + assert (output_dir / "project").is_dir() + assert (output_dir / "project" / "a.py").is_file() + assert (output_dir / "project" / "b.txt").is_file() diff --git a/tests/test_yaml_parser_extended.py b/tests/test_yaml_parser_extended.py new file mode 100644 index 0000000..e4d9760 --- /dev/null +++ b/tests/test_yaml_parser_extended.py @@ -0,0 +1,78 @@ +import pytest +from nirman.yaml_parser import parse_yaml_tree + + +def test_yaml_simple_files_under_files_key(): + yaml_data = """ +app: + files: + - a.py + - b.txt +""" + expected = [ + (0, "app", True), + (1, "a.py", False), + (1, "b.txt", False), + ] + + assert parse_yaml_tree(yaml_data) == expected + + +def test_yaml_nested_structure_with_files(): + yaml_data = """ +project: + src: + files: + - main.py + - config.json + utils: + helpers: + files: + - helper.py +""" + expected = [ + (0, "project", True), + (1, "src", True), + (2, "main.py", False), + (2, "config.json", False), + (1, "utils", True), + (2, "helpers", True), + (3, "helper.py", False), + ] + + assert parse_yaml_tree(yaml_data) == expected + + +def test_yaml_dict_without_files_is_folder(): + yaml_data = """ +root: + empty_folder: {} +""" + expected = [ + (0, "root", True), + (1, "empty_folder", True), + ] + assert parse_yaml_tree(yaml_data) == expected + + +def test_yaml_list_of_mixed_items(): + yaml_data = """ +root: + items: + - folder1: + files: + - a.py + - folder2: + files: + - b.py +""" + expected = [ + (0, "root", True), + (1, "items", True), + (2, "folder1", True), + (3, "a.py", False), + (2, "folder2", True), + (3, "b.py", False), + ] + + assert parse_yaml_tree(yaml_data) == expected