tldr_cli/commands/
cognitive.rs1use std::path::PathBuf;
8
9use anyhow::Result;
10use clap::Args;
11
12use tldr_core::{
13 metrics::{
14 analyze_cognitive, merge_cognitive_reports, walk_source_files, CognitiveOptions,
15 WalkOptions,
16 },
17 validate_file_path, Language,
18};
19
20use crate::output::{format_cognitive_text, OutputFormat, OutputWriter};
21
22#[derive(Debug, Args)]
24pub struct CognitiveArgs {
25 #[arg(default_value = ".")]
27 pub path: PathBuf,
28
29 #[arg(long)]
32 pub function: Option<String>,
33
34 #[arg(long, short = 'l')]
36 pub lang: Option<Language>,
37
38 #[arg(long, default_value = "15")]
40 pub threshold: u32,
41
42 #[arg(long, default_value = "25")]
44 pub high_threshold: u32,
45
46 #[arg(long)]
48 pub show_contributors: bool,
49
50 #[arg(long)]
52 pub include_cyclomatic: bool,
53
54 #[arg(long, default_value = "50")]
56 pub top: usize,
57
58 #[arg(long, short = 'e')]
60 pub exclude: Vec<String>,
61
62 #[arg(long)]
64 pub include_hidden: bool,
65
66 #[arg(long, default_value = "0")]
68 pub max_files: usize,
69}
70
71impl CognitiveArgs {
72 pub fn run(&self, format: OutputFormat, quiet: bool) -> Result<()> {
74 let writer = OutputWriter::new(format, quiet);
75
76 let options = CognitiveOptions::new()
78 .with_function(self.function.clone())
79 .with_threshold(self.threshold)
80 .with_high_threshold(self.high_threshold)
81 .with_contributors(self.show_contributors)
82 .with_cyclomatic(self.include_cyclomatic)
83 .with_top(self.top);
84
85 let report = if self.path.is_file() {
86 let validated_path = validate_file_path(self.path.to_str().unwrap_or_default(), None)?;
88
89 writer.progress(&format!(
90 "Calculating cognitive complexity for {}...",
91 validated_path.display()
92 ));
93
94 analyze_cognitive(&validated_path, &options)?
95 } else if self.path.is_dir() {
96 let walk_options = WalkOptions {
98 lang: self.lang,
99 exclude: self.exclude.clone(),
100 include_hidden: self.include_hidden,
101 gitignore: true,
102 max_files: self.max_files,
103 };
104
105 let (files, walk_warnings) = walk_source_files(&self.path, &walk_options)?;
106
107 writer.progress(&format!(
108 "Analyzing {} files in {}...",
109 files.len(),
110 self.path.display()
111 ));
112
113 let mut reports = Vec::new();
114 let mut extra_warnings = walk_warnings;
115
116 for file in &files {
117 match analyze_cognitive(file, &options) {
118 Ok(report) => reports.push(report),
119 Err(e) => {
120 extra_warnings.push(format!("Failed to analyze {}: {}", file.display(), e));
121 }
122 }
123 }
124
125 let mut merged = merge_cognitive_reports(reports, &options);
126 let mut all_warnings = extra_warnings;
128 all_warnings.append(&mut merged.warnings);
129 merged.warnings = all_warnings;
130 merged
131 } else {
132 return Err(anyhow::anyhow!(
134 "Path does not exist: {}",
135 self.path.display()
136 ));
137 };
138
139 if writer.is_text() {
141 writer.write_text(&format_cognitive_text(&report))?;
142 } else {
143 writer.write(&report)?;
144 }
145
146 Ok(())
147 }
148}