hashdeep_compare/
main_impl.rs1use crate::*;
37use std::error::Error;
38use std::io::Write;
39use clap::{Parser, Subcommand};
40
41pub fn main_io_wrapper(
49 args: &[&str],
50 mut stdout: impl Write,
51 mut stderr: impl Write,
52) -> Result<i32, Box<dyn Error>> {
53
54 let exit_code =
55 match main_impl(args, &mut stdout, &mut stderr)
56 {
57 Ok(()) => 0,
58 Err(err) => {
59 if let Some(err) = err.downcast_ref::<clap::Error>() {
60 if err.use_stderr() {
61 write! (stderr, "{err}")?;
62 2
64 }
65 else {
66 write! (stdout, "{err}")?;
67 0
68 }
69 }
70 else {
71 if let Some(err) = err.downcast_ref::<command::RunHashdeepCommandError>() {
73 writeln! (stderr, "Error: \"{err}\"")?;
74 }
75 else if let Some(err) = err.downcast_ref::<common::ReadLogEntriesFromFileError>() {
76 writeln! (stderr, "Error: \"{err}\"")?;
77 }
78 else if let Some(err) = err.downcast_ref::<common::WriteToFileError>() {
79 writeln! (stderr, "Error: \"{err}\"")?;
80 }
81 else if let Some(err) = err.downcast_ref::<partitioner::MatchPartitionError>() {
82 writeln! (stderr, "Error: \"{err}\"")?;
83 }
84 else {
85 writeln! (stderr, "Error: {err:?}")?;
86 }
87
88 1
89 }
90 }
91 };
92
93 Ok(exit_code)
94}
95
96
97fn print_hashdeep_log_warnings (
98 filename: &str,
99 warning_lines: Option<Vec<String>>,
100 stderr: &mut impl Write) -> Result<(), Box<dyn Error>>
101{
102 if let Some(v) = warning_lines {
103 writeln!(stderr, "Warnings emitted for hashdeep log at: {filename}")?;
104 for line in v {
105 writeln!(stderr, " {line}")?;
106 }
107 }
108 Ok(())
109}
110
111fn write_lines (writer: &mut impl Write, lines: Vec<String>) -> Result<(), Box<dyn Error>> {
112 lines.iter().try_for_each(|line| writeln!(writer, "{line}").map_err(Into::into))
113}
114
115fn main_impl(args: &[&str], stdout: &mut impl Write, stderr: &mut impl Write) -> Result<(), Box<dyn Error>> {
119
120 const VERSION: &str = env!("CARGO_PKG_VERSION");
121
122 #[derive(Parser, Debug)]
123 #[command(about = help::help_string(VERSION))]
124 struct CliArgs {
125 #[command(subcommand)]
126 command: Commands,
127 }
128
129 #[derive(Subcommand, Debug)]
130 #[command(disable_help_flag = true)]
131 enum Commands {
132 Version,
134 #[command(after_long_help = help::help_hash_string())]
135 #[command(long_about = help::long_about_hash_string())]
136 Hash {
138 #[arg(hide_long_help = true, id="path/to/target_dir")]
139 target_directory: String,
140 #[arg(hide_long_help = true, id="path/to/output_log.txt")]
141 output_path_base: String,
142 },
143 #[command(after_long_help = help::help_sort_string())]
144 #[command(long_about = help::long_about_sort_string())]
145 Sort {
147 #[arg(hide_long_help = true, id="path/to/unsorted_input.txt")]
148 input_file: String,
149 #[arg(hide_long_help = true, id="path/to/sorted_output.txt")]
150 output_file: String,
151 },
152 #[command(after_long_help = help::help_root_string())]
153 #[command(long_about = help::long_about_root_string())]
154 Root {
156 #[arg(hide_long_help = true, id="path/to/input.txt")]
157 input_file: String,
158 #[arg(hide_long_help = true, id="path/to/output.txt")]
159 output_file: String,
160 #[arg(hide_long_help = true, id="filepath prefix")]
161 file_path_prefix: String,
162 },
163 #[command(after_long_help = help::help_part_string())]
164 #[command(long_about = help::long_about_part_string())]
165 Part {
167 #[arg(hide_long_help = true, id="path/to/first_log.txt")]
168 input_file1: String,
169 #[arg(hide_long_help = true, id="path/to/second_log.txt")]
170 input_file2: String,
171 #[arg(hide_long_help = true, id="path/to/output_file_base")]
172 output_file_base: String,
173 },
174 }
175
176 let cli_args = CliArgs::try_parse_from(args)?;
177
178 match cli_args.command {
179 Commands::Hash {target_directory, output_path_base} => {
180 command::run_hashdeep_command(
181 target_directory.as_str(),
182 output_path_base.as_str(),
183 "hashdeep")?;
184 },
185 Commands::Sort {input_file, output_file} => {
186 let warning_lines = sort::sort_log(
187 input_file.as_str(),
188 output_file.as_str()
189 )?;
190 print_hashdeep_log_warnings(input_file.as_str(), warning_lines, stderr)?;
191 },
192 Commands::Root {input_file, output_file, file_path_prefix} => {
193 let success = root::change_root(
194 input_file.as_str(),
195 output_file.as_str(),
196 file_path_prefix.as_str(),
197 )?;
198 write_lines(stdout, success.info_lines)?;
199 write_lines(stderr, success.warning_lines)?;
200 print_hashdeep_log_warnings(input_file.as_str(), success.file_warning_lines, stderr)?;
201 },
202 Commands::Part {input_file1, input_file2, output_file_base} => {
203 let partition_stats =
204 partition::partition_log(
205 input_file1.as_str(),
206 input_file2.as_str(),
207 output_file_base.as_str()
208 )?;
209
210 writeln!(stdout, "{}", partition_stats.stats_string)?;
211 print_hashdeep_log_warnings(input_file1.as_str(), partition_stats.file1_warning_lines, stderr)?;
212 print_hashdeep_log_warnings(input_file2.as_str(), partition_stats.file2_warning_lines, stderr)?;
213 },
214 Commands::Version => {
215 writeln!(stdout, "hashdeep-compare version {VERSION}")?;
216 }
217 }
218
219 Ok(())
220}