Skip to main content

tldr_cli/commands/
detect_patterns.rs

1//! Patterns command - Detect design patterns and coding conventions
2//!
3//! Analyzes a codebase to detect patterns including:
4//! - Soft delete patterns
5//! - Error handling patterns
6//! - Naming conventions
7//! - Resource management patterns
8//! - Validation patterns
9//! - Test idioms
10//! - Import patterns
11//! - Type coverage
12//! - API conventions
13//! - Async patterns
14
15use std::path::PathBuf;
16
17use anyhow::Result;
18use clap::Args;
19
20use tldr_core::patterns::format::format_pattern_report_text;
21use tldr_core::{detect_patterns_with_config, Language, PatternCategory, PatternConfig};
22
23use crate::output::{OutputFormat, OutputWriter};
24
25/// Detect design patterns and coding conventions
26#[derive(Debug, Args)]
27pub struct PatternsArgs {
28    /// Path to file or directory to analyze (default: current directory)
29    #[arg(default_value = ".")]
30    pub path: PathBuf,
31
32    /// Programming language (auto-detect if not specified)
33    #[arg(long, short = 'l')]
34    pub lang: Option<Language>,
35
36    /// Filter to specific pattern category
37    #[arg(long, short = 'c', value_parser = parse_category)]
38    pub category: Option<PatternCategory>,
39
40    /// Minimum confidence threshold (0.0-1.0)
41    #[arg(long, default_value = "0.5")]
42    pub min_confidence: f64,
43
44    /// Maximum files to analyze (0 = unlimited)
45    #[arg(long, default_value = "1000")]
46    pub max_files: usize,
47
48    /// Skip LLM constraint generation
49    #[arg(long)]
50    pub no_constraints: bool,
51}
52
53fn parse_category(s: &str) -> Result<PatternCategory, String> {
54    s.parse()
55}
56
57impl PatternsArgs {
58    /// Run the patterns command
59    pub fn run(&self, format: OutputFormat, quiet: bool) -> Result<()> {
60        let writer = OutputWriter::new(format, quiet);
61
62        writer.progress(&format!("Analyzing patterns in {}...", self.path.display()));
63
64        // Build config
65        let config = PatternConfig {
66            min_confidence: self.min_confidence,
67            max_files: self.max_files,
68            evidence_limit: 3,
69            categories: self.category.map(|c| vec![c]).unwrap_or_default(),
70            generate_constraints: !self.no_constraints,
71        };
72
73        // Run pattern detection
74        let report = detect_patterns_with_config(&self.path, self.lang, config)?;
75
76        // Output based on format
77        if writer.is_text() {
78            let text = format_pattern_report_text(&report);
79            writer.write_text(&text)?;
80        } else {
81            writer.write(&report)?;
82        }
83
84        Ok(())
85    }
86}