Project Structure
RepositoryThis CLI tool is a fast directory tree generator. Visualize your project structure in ASCII while respecting .gitignore rules, with tons of additional flags and options to customize your output.
cli.rs - Custom Flags
Here you can see the different flags and options implemented in cli.rs. You can use these for the CLI tool to customize the output—from automated sorting to writing to a Markdown file or limiting the tree depth, everything is possible.
1#[derive(Parser, Clone)]
2#[command(name = "project-structure")]
3pub struct Cli {
4 /// Root path of the project
5 path: Option<PathBuf>,
6
7 /// Include hidden files and directories
8 #[arg(long)]
9 pub include_hidden: bool,
10
11 /// Do not use .gitignore rules
12 #[arg(long)]
13 pub no_git: bool,
14
15 /// Space-seperated exclude patterns (gitignore-style)
16 #[arg(long)]
17 pub exclude: Option<String>,
18
19 /// Filter Depth
20 #[arg(long)]
21 pub depth: Option<usize>,
22
23 /// Sort Results flag
24 #[arg(long, value_enum, default_value = "name")]
25 pub sort: SortBy,
26
27 /// Filter for just files or directories
28 #[arg(long, value_enum)]
29 pub filter: Option<Filter>,
30
31 /// Output written to file instead of terminal
32 #[arg(short, long, num_args(0..=1), require_equals(true))]
33 pub output: Option<Option<PathBuf>>,
34}
main.rs - The Core of the Project
In this main function, you can see the modular architecture of the project. Instead of having one giant function, it breaks the logic down into different modules that handle specific tasks before passing the data to the next step.
1fn main() -> Result<()> {
2 let cli = Cli::parse();
3 let config = Config::from(cli.clone());
4
5 let mut nodes = walker::walk(&config)?;
6 config.sort_nodes(&mut nodes);
7
8 let tree = tree::build(&nodes, &config);
9
10 // Get the current directory name to use as the root display name
11 let current_dir = env::current_dir()?;
12 let root_name = current_dir
13 .file_name()
14 .and_then(|s| s.to_str())
15 .unwrap_or("parentdirectory"); // Fallback name if current_dir has no file_name
16
17 let mut modified_tree = tree;
18 modified_tree.path = PathBuf::from(format!("{}/", root_name));
19
20 let output = render::ascii(&modified_tree, &config.filter);
21
22 let file_path: Option<PathBuf> = match &cli.output {
23 Some(Some(path)) => Some(path.clone()),
24 Some(None) => Some("Project Structure.md".into()),
25 None => None,
26 };
27
28 if let Some(path) = file_path {
29 let markdown = format!("# Project Structure\n\n```\n{}\n```", output);
30 fs::write(&path, markdown)?;
31 println!("Project structure written to {}", path.display());
32 } else {
33 println!("{}", output);
34 }
35
36 Ok(())
37}
Example Showcase
Below is an example of the output generated by this project. It creates a clean, git-aware ASCII representation of the directory while excluding bloat folders such as target or node_modules.
1project-structure/ 2├── src/ 3│ ├── cli.rs 4│ ├── config.rs 5│ ├── main.rs 6│ ├── node.rs 7│ ├── render.rs 8│ ├── tree.rs 9│ └── walker.rs 10├── Cargo.lock 11├── Cargo.toml 12├── Example.md 13├── LICENSE 14└── README.md