Skip to content

Commit 01f3da9

Browse files
committed
Merge branch 'add-checksum-util'
2 parents 50c500e + b8ad5d9 commit 01f3da9

File tree

1 file changed

+164
-0
lines changed

1 file changed

+164
-0
lines changed

src/bin/checksum.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//! This is a simple program to calculate a checksum from the command line
2+
3+
use crc_fast::{checksum, checksum_file, CrcAlgorithm};
4+
use std::env;
5+
use std::process::ExitCode;
6+
use std::str::FromStr;
7+
8+
#[derive(Debug)]
9+
struct Config {
10+
algorithm: String,
11+
file: Option<String>,
12+
string: Option<String>,
13+
format: OutputFormat,
14+
}
15+
16+
#[derive(Debug, Clone)]
17+
enum OutputFormat {
18+
Hex,
19+
Decimal,
20+
}
21+
22+
fn print_usage() {
23+
println!("Usage: checksum -a algorithm [-f file] [-s string] [--format hex|decimal]");
24+
println!();
25+
println!("Example: checksum -a CRC-32/ISCSI -f myfile.txt");
26+
println!("Example: checksum -a CRC-64/NVME -s 'Hello, world!' --format decimal");
27+
println!();
28+
println!("Options:");
29+
println!(" -a algorithm Specify the checksum algorithm (required)");
30+
println!(" -f file Calculate checksum for the specified file");
31+
println!(" -s string Calculate checksum for the specified string");
32+
println!(" --format hex|decimal Output format (default: hex)");
33+
println!(" -h, --help Show this help message");
34+
println!();
35+
println!("Note: Either -f or -s must be provided, but not both.");
36+
}
37+
38+
fn parse_args() -> Result<Config, String> {
39+
let args: Vec<String> = env::args().collect();
40+
41+
if args.len() == 1 {
42+
return Err("No arguments provided".to_string());
43+
}
44+
45+
// Check for help flag
46+
if args.contains(&"-h".to_string()) || args.contains(&"--help".to_string()) {
47+
return Err("help".to_string());
48+
}
49+
50+
let mut algorithm: Option<String> = None;
51+
let mut file: Option<String> = None;
52+
let mut string: Option<String> = None;
53+
let mut format = OutputFormat::Hex; // Default to hex
54+
55+
let mut i = 1; // Skip program name
56+
while i < args.len() {
57+
match args[i].as_str() {
58+
"-a" => {
59+
if i + 1 >= args.len() {
60+
return Err("Missing algorithm after -a flag".to_string());
61+
}
62+
algorithm = Some(args[i + 1].clone());
63+
i += 2;
64+
}
65+
"-f" => {
66+
if i + 1 >= args.len() {
67+
return Err("Missing filename after -f flag".to_string());
68+
}
69+
if string.is_some() {
70+
return Err("Cannot specify both -f and -s flags".to_string());
71+
}
72+
file = Some(args[i + 1].clone());
73+
i += 2;
74+
}
75+
"-s" => {
76+
if i + 1 >= args.len() {
77+
return Err("Missing string after -s flag".to_string());
78+
}
79+
if file.is_some() {
80+
return Err("Cannot specify both -f and -s flags".to_string());
81+
}
82+
string = Some(args[i + 1].clone());
83+
i += 2;
84+
}
85+
"--format" => {
86+
if i + 1 >= args.len() {
87+
return Err("Missing format after --format flag".to_string());
88+
}
89+
match args[i + 1].as_str() {
90+
"hex" => format = OutputFormat::Hex,
91+
"decimal" => format = OutputFormat::Decimal,
92+
invalid => {
93+
return Err(format!(
94+
"Invalid format '{}'. Use 'hex' or 'decimal'",
95+
invalid
96+
))
97+
}
98+
}
99+
i += 2;
100+
}
101+
arg => {
102+
return Err(format!("Unknown argument: {}", arg));
103+
}
104+
}
105+
}
106+
107+
// Validate required arguments
108+
let algorithm = algorithm.ok_or("Algorithm (-a) is required")?;
109+
110+
if file.is_none() && string.is_none() {
111+
return Err("Either -f (file) or -s (string) must be provided".to_string());
112+
}
113+
114+
Ok(Config {
115+
algorithm,
116+
file,
117+
string,
118+
format,
119+
})
120+
}
121+
122+
fn calculate_checksum(config: &Config) -> Result<(), String> {
123+
let algorithm = CrcAlgorithm::from_str(&config.algorithm)
124+
.map_err(|_| format!("Invalid algorithm: {}", config.algorithm))?;
125+
126+
let checksum = if let Some(ref filename) = config.file {
127+
checksum_file(algorithm, filename, None).unwrap()
128+
} else if let Some(ref text) = config.string {
129+
checksum(algorithm, text.as_bytes())
130+
} else {
131+
return Err("No input provided for checksum calculation".to_string());
132+
};
133+
134+
match config.format {
135+
OutputFormat::Hex => println!("{:#x?}", checksum),
136+
OutputFormat::Decimal => println!("{}", checksum),
137+
}
138+
139+
Ok(())
140+
}
141+
142+
fn main() -> ExitCode {
143+
match parse_args() {
144+
Ok(config) => {
145+
if let Err(e) = calculate_checksum(&config) {
146+
eprintln!("Error: {}", e);
147+
return ExitCode::from(1);
148+
}
149+
}
150+
Err(msg) => {
151+
if msg == "help" {
152+
print_usage();
153+
return ExitCode::SUCCESS;
154+
} else {
155+
eprintln!("Error: {}", msg);
156+
println!();
157+
print_usage();
158+
return ExitCode::from(1);
159+
}
160+
}
161+
}
162+
163+
ExitCode::SUCCESS
164+
}

0 commit comments

Comments
 (0)