Skip to main content

tldr_cli/commands/
structure.rs

1//! Structure command - Show code structure
2//!
3//! Extracts and displays functions, classes, and imports from source files.
4//! Auto-routes through daemon when available for ~35x speedup.
5
6use std::path::PathBuf;
7
8use anyhow::Result;
9use clap::Args;
10
11use tldr_core::types::CodeStructure;
12use tldr_core::{get_code_structure, IgnoreSpec, Language};
13
14use crate::commands::daemon_router::{params_with_path_lang, try_daemon_route};
15use crate::output::{format_structure_text, OutputFormat, OutputWriter};
16
17/// Extract code structure (functions, classes, imports)
18#[derive(Debug, Args)]
19pub struct StructureArgs {
20    /// Directory to scan (default: current directory)
21    #[arg(default_value = ".")]
22    pub path: PathBuf,
23
24    /// Programming language (auto-detected if not specified)
25    #[arg(long, short = 'l')]
26    pub lang: Option<Language>,
27
28    /// Maximum number of files to process (0 = unlimited)
29    #[arg(long, short = 'm', default_value = "0")]
30    pub max_results: usize,
31}
32
33impl StructureArgs {
34    /// Run the structure command
35    pub fn run(&self, format: OutputFormat, quiet: bool) -> Result<()> {
36        let writer = OutputWriter::new(format, quiet);
37
38        // Validate path exists BEFORE language detection / progress banner
39        // (lang-detect-default-v1: avoid printing misleading "(Python)" banner
40        // when the path doesn't exist and from_directory silently returns None.)
41        if !self.path.exists() {
42            anyhow::bail!("Path not found: {}", self.path.display());
43        }
44
45        // Determine language (auto-detect from directory, default to Python)
46        let language = self
47            .lang
48            .unwrap_or_else(|| Language::from_directory(&self.path).unwrap_or(Language::Python));
49
50        // Try daemon first for cached result
51        if let Some(structure) = try_daemon_route::<CodeStructure>(
52            &self.path,
53            "structure",
54            params_with_path_lang(&self.path, Some(language.as_str())),
55        ) {
56            // Output based on format
57            if writer.is_text() {
58                let text = format_structure_text(&structure);
59                writer.write_text(&text)?;
60                return Ok(());
61            } else {
62                writer.write(&structure)?;
63                return Ok(());
64            }
65        }
66
67        // Fallback to direct compute
68        writer.progress(&format!(
69            "Extracting structure from {} ({:?})...",
70            self.path.display(),
71            language
72        ));
73
74        // Get code structure
75        let structure = get_code_structure(
76            &self.path,
77            language,
78            self.max_results,
79            Some(&IgnoreSpec::default()),
80        )?;
81
82        // Output based on format
83        if writer.is_text() {
84            let text = format_structure_text(&structure);
85            writer.write_text(&text)?;
86        } else {
87            writer.write(&structure)?;
88        }
89
90        Ok(())
91    }
92}