dry4rust/
command_dispatcher.rs1use std::io::Write;
7use std::path::Path;
8
9use crate::analyzer::LanguageAnalyzer;
10use crate::cli::analysis_output::AnalysisOutput;
11use crate::cli::baseline_command::BaselineCommand;
12use crate::cli::check_command::CheckCommand;
13use crate::cli::checking::check_thresholds::CheckThresholds;
14use crate::cli::cleanup_command::CleanupCommand;
15use crate::cli::cli_error::CliError;
16use crate::cli::cli_error::CliResult;
17use crate::cli::cli_overrides::CliOverrides;
18use crate::cli::command::Command;
19use crate::cli::ignore_command::IgnoreCommand;
20use crate::cli::ignored_command::IgnoredCommand;
21use crate::cli::output_format::OutputFormat;
22use crate::cli::report_command::ReportCommand;
23use crate::cli::stats_command::StatsCommand;
24use crate::output::reporter::Reporter;
25
26pub struct CommandDispatcher<'a> {
33 analyzer: &'a dyn LanguageAnalyzer,
34 root: &'a Path,
35 format: OutputFormat,
36 overrides: CliOverrides,
37}
38
39impl<'a> CommandDispatcher<'a> {
40 #[must_use]
41 pub const fn new(
42 analyzer: &'a dyn LanguageAnalyzer,
43 root: &'a Path,
44 format: OutputFormat,
45 overrides: CliOverrides,
46 ) -> Self {
47 Self {
48 analyzer,
49 root,
50 format,
51 overrides,
52 }
53 }
54
55 pub fn dispatch(&self, command: &Command, writer: &mut impl Write) -> CliResult {
63 match command {
64 Command::Ignore {
65 fingerprint,
66 reason,
67 } => IgnoreCommand::new(self.root, fingerprint, reason.as_deref()).run(writer),
68 Command::Ignored => IgnoredCommand::new(self.root).run(writer),
69 _ => self.dispatch_analysed(command, writer),
70 }
71 }
72
73 fn dispatch_analysed(&self, command: &Command, writer: &mut impl Write) -> CliResult {
74 let output = if matches!(command, Command::Baseline { .. }) {
75 AnalysisOutput::produce_ignoring_baseline(
76 self.analyzer,
77 self.root,
78 self.format,
79 &self.overrides,
80 )?
81 } else {
82 AnalysisOutput::produce(self.analyzer, self.root, self.format, &self.overrides)?
83 };
84 Self::report_warnings(&output);
85 self.render(command, &output, writer)
86 }
87
88 fn report_warnings(output: &AnalysisOutput) {
89 for warning in &output.result.warnings {
90 eprintln!("Warning: {warning}");
91 }
92 }
93
94 fn render(
95 &self,
96 command: &Command,
97 output: &AnalysisOutput,
98 writer: &mut impl Write,
99 ) -> CliResult {
100 let reporter: &dyn Reporter = &*output.reporter;
101 match command {
102 Command::Stats => StatsCommand::new(&output.result, reporter).run(writer),
103 Command::Report => ReportCommand::new(&output.result, reporter).run(writer),
104 Command::Check {
105 max_exact,
106 max_near,
107 max_exact_percent,
108 max_near_percent,
109 } => {
110 let thresholds = CheckThresholds::new(
111 *max_exact,
112 *max_near,
113 *max_exact_percent,
114 *max_near_percent,
115 )
116 .map_err(|e| CliError::InvalidConfig(e.to_string()))?;
117 CheckCommand::new(&output.config, &output.result, reporter, &thresholds).run(writer)
118 }
119 Command::Cleanup { dry_run } => {
120 CleanupCommand::new(self.root, &output.result, *dry_run).run(writer)
121 }
122 Command::Baseline { dry_run } => {
123 BaselineCommand::new(self.root, &output.config, &output.result, *dry_run)
124 .run(writer)
125 }
126 Command::Ignore { .. } | Command::Ignored => unreachable!(),
127 }
128 }
129}