Skip to main content

tldr_cli/commands/
tree.rs

1//! Tree command - Show file tree
2//!
3//! Displays the file tree structure of a directory.
4//! Auto-routes through daemon when available for ~35x speedup.
5
6use std::collections::HashSet;
7use std::path::PathBuf;
8
9use anyhow::Result;
10use clap::Args;
11
12use tldr_core::types::FileTree;
13use tldr_core::{get_file_tree, IgnoreSpec};
14
15use crate::commands::daemon_router::{params_with_path, try_daemon_route};
16use crate::output::{format_file_tree_text, OutputFormat, OutputWriter};
17
18/// Show file tree structure
19#[derive(Debug, Args)]
20pub struct TreeArgs {
21    /// Directory to scan (default: current directory)
22    #[arg(default_value = ".")]
23    pub path: PathBuf,
24
25    /// Filter by file extensions (e.g., --ext .py --ext .rs)
26    #[arg(long = "ext", short = 'e')]
27    pub extensions: Vec<String>,
28
29    /// Include hidden files and directories
30    #[arg(long, short = 'H')]
31    pub include_hidden: bool,
32}
33
34impl TreeArgs {
35    /// Run the tree command
36    pub fn run(&self, format: OutputFormat, quiet: bool) -> Result<()> {
37        let writer = OutputWriter::new(format, quiet);
38
39        // Try daemon first for cached result
40        if let Some(tree) =
41            try_daemon_route::<FileTree>(&self.path, "tree", params_with_path(Some(&self.path)))
42        {
43            // Output based on format
44            if writer.is_text() {
45                let text = format_file_tree_text(&tree, 0);
46                writer.write_text(&text)?;
47                return Ok(());
48            } else {
49                writer.write(&tree)?;
50                return Ok(());
51            }
52        }
53
54        // Fallback to direct compute
55
56        // Build extensions set if provided
57        let extensions: Option<HashSet<String>> = if self.extensions.is_empty() {
58            None
59        } else {
60            Some(
61                self.extensions
62                    .iter()
63                    .map(|s| {
64                        if s.starts_with('.') {
65                            s.clone()
66                        } else {
67                            format!(".{}", s)
68                        }
69                    })
70                    .collect(),
71            )
72        };
73
74        // Get file tree
75        let tree = get_file_tree(
76            &self.path,
77            extensions.as_ref(),
78            !self.include_hidden,
79            Some(&IgnoreSpec::default()),
80        )?;
81
82        // Output based on format
83        if writer.is_text() {
84            let text = format_file_tree_text(&tree, 0);
85            writer.write_text(&text)?;
86        } else {
87            writer.write(&tree)?;
88        }
89
90        Ok(())
91    }
92}