1use std::path::PathBuf;
7
8use anyhow::Result;
9use clap::Args;
10
11use tldr_core::{
12 analyze_smells_aggregated_with_walker_opts, detect_smells_with_walker_opts, Language,
13 SmellType, SmellsReport, SmellsWalkerOpts, ThresholdPreset,
14};
15
16use crate::commands::daemon_router::{params_with_path, try_daemon_route};
17use crate::output::{format_smells_text, OutputFormat, OutputWriter};
18
19#[derive(Debug, Args)]
21pub struct SmellsArgs {
22 #[arg(default_value = ".")]
24 pub path: PathBuf,
25
26 #[arg(long, short = 'l')]
28 pub lang: Option<Language>,
29
30 #[arg(long, short = 't', default_value = "default")]
32 pub threshold: ThresholdPresetArg,
33
34 #[arg(long, short = 's')]
36 pub smell_type: Option<SmellTypeArg>,
37
38 #[arg(long)]
40 pub suggest: bool,
41
42 #[arg(long)]
46 pub deep: bool,
47
48 #[arg(long)]
50 pub no_default_ignore: bool,
51}
52
53#[derive(Debug, Clone, Copy, Default, clap::ValueEnum)]
55pub enum ThresholdPresetArg {
56 Strict,
58 #[default]
60 Default,
61 Relaxed,
63}
64
65impl From<ThresholdPresetArg> for ThresholdPreset {
66 fn from(arg: ThresholdPresetArg) -> Self {
67 match arg {
68 ThresholdPresetArg::Strict => ThresholdPreset::Strict,
69 ThresholdPresetArg::Default => ThresholdPreset::Default,
70 ThresholdPresetArg::Relaxed => ThresholdPreset::Relaxed,
71 }
72 }
73}
74
75#[derive(Debug, Clone, Copy, clap::ValueEnum)]
77pub enum SmellTypeArg {
78 GodClass,
80 LongMethod,
82 LongParameterList,
84 FeatureEnvy,
86 DataClumps,
88 LowCohesion,
90 TightCoupling,
92 DeadCode,
94 CodeClone,
96 HighCognitiveComplexity,
98 DeepNesting,
100 DataClass,
102 LazyElement,
104 MessageChain,
106 PrimitiveObsession,
108 MiddleMan,
110 RefusedBequest,
112 InappropriateIntimacy,
114}
115
116impl From<SmellTypeArg> for SmellType {
117 fn from(arg: SmellTypeArg) -> Self {
118 match arg {
119 SmellTypeArg::GodClass => SmellType::GodClass,
120 SmellTypeArg::LongMethod => SmellType::LongMethod,
121 SmellTypeArg::LongParameterList => SmellType::LongParameterList,
122 SmellTypeArg::FeatureEnvy => SmellType::FeatureEnvy,
123 SmellTypeArg::DataClumps => SmellType::DataClumps,
124 SmellTypeArg::LowCohesion => SmellType::LowCohesion,
125 SmellTypeArg::TightCoupling => SmellType::TightCoupling,
126 SmellTypeArg::DeadCode => SmellType::DeadCode,
127 SmellTypeArg::CodeClone => SmellType::CodeClone,
128 SmellTypeArg::HighCognitiveComplexity => SmellType::HighCognitiveComplexity,
129 SmellTypeArg::DeepNesting => SmellType::DeepNesting,
130 SmellTypeArg::DataClass => SmellType::DataClass,
131 SmellTypeArg::LazyElement => SmellType::LazyElement,
132 SmellTypeArg::MessageChain => SmellType::MessageChain,
133 SmellTypeArg::PrimitiveObsession => SmellType::PrimitiveObsession,
134 SmellTypeArg::MiddleMan => SmellType::MiddleMan,
135 SmellTypeArg::RefusedBequest => SmellType::RefusedBequest,
136 SmellTypeArg::InappropriateIntimacy => SmellType::InappropriateIntimacy,
137 }
138 }
139}
140
141impl SmellsArgs {
142 pub fn run(&self, format: OutputFormat, quiet: bool) -> Result<()> {
144 let writer = OutputWriter::new(format, quiet);
145
146 if let Some(report) = try_daemon_route::<SmellsReport>(
148 &self.path,
149 "smells",
150 params_with_path(Some(&self.path)),
151 ) {
152 if writer.is_text() {
154 let text = format_smells_text(&report);
155 writer.write_text(&text)?;
156 return Ok(());
157 } else {
158 writer.write(&report)?;
159 return Ok(());
160 }
161 }
162
163 writer.progress(&format!(
165 "Scanning for code smells in {}{}...",
166 self.path.display(),
167 if self.deep { " (deep analysis)" } else { "" }
168 ));
169
170 let walker_opts = SmellsWalkerOpts {
172 no_default_ignore: self.no_default_ignore,
173 lang: self.lang,
174 };
175 let report = if self.deep {
176 analyze_smells_aggregated_with_walker_opts(
177 &self.path,
178 self.threshold.into(),
179 self.smell_type.map(|s| s.into()),
180 self.suggest,
181 walker_opts,
182 )?
183 } else {
184 detect_smells_with_walker_opts(
185 &self.path,
186 self.threshold.into(),
187 self.smell_type.map(|s| s.into()),
188 self.suggest,
189 walker_opts,
190 )?
191 };
192
193 if writer.is_text() {
195 let text = format_smells_text(&report);
196 writer.write_text(&text)?;
197 } else {
198 writer.write(&report)?;
199 }
200
201 Ok(())
202 }
203}