use std::{
fs::read,
io::{self, BufRead},
path::Path,
};
forc_util::cli_examples! {
crate::Command {
[ Hashes an argument with SHA256 => "forc crypto sha256 test" ]
[ Hashes an argument with Keccak256 => "forc crypto keccak256 test" ]
[ Hashes a file path with SHA256 => "forc crypto sha256 {file}" ]
[ Hashes a file path with Keccak256 => "forc crypto keccak256 {file}" ]
}
}
#[derive(Debug, Clone, clap::Args)]
#[clap(
version,
about = "Hashes the argument or file with this algorithm",
after_help = help(),
)]
pub struct HashArgs {
content_or_filepath: Option<String>,
}
fn checked_read_file<P: AsRef<Path>>(path: &Option<P>) -> Option<Vec<u8>> {
path.as_ref().map(read)?.ok()
}
fn checked_read_stdin<R: BufRead>(content: &Option<String>, mut stdin: R) -> Option<Vec<u8>> {
match content.as_ref().map(|x| x.as_str()) {
Some("-") | None => {
let mut buffer = Vec::new();
if stdin.read_to_end(&mut buffer).is_ok() {
Some(buffer)
} else {
Some(vec![])
}
}
_ => None,
}
}
fn read_as_binary(content: &Option<String>) -> Vec<u8> {
content
.as_ref()
.map(|x| {
if let Some(hex) = x.trim().strip_prefix("0x") {
if let Ok(bin) = hex::decode(hex) {
bin
} else {
x.as_bytes().to_vec()
}
} else {
x.as_bytes().to_vec()
}
})
.unwrap_or_default()
}
pub fn read_content_filepath_or_stdin(arg: Option<String>) -> Vec<u8> {
match checked_read_file(&arg) {
Some(bytes) => bytes,
None => match checked_read_stdin(&arg, io::stdin().lock()) {
Some(bytes) => bytes,
None => read_as_binary(&arg),
},
}
}
impl From<HashArgs> for Vec<u8> {
fn from(value: HashArgs) -> Self {
read_content_filepath_or_stdin(value.content_or_filepath)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_checked_read_file() {
assert!(checked_read_file(&Some("not a file")).is_none());
assert!(checked_read_file(&Some("Cargo.toml")).is_some());
assert!(checked_read_file::<String>(&None).is_none());
}
#[test]
fn test_checked_stdin() {
let stdin = b"I'm a test from stdin";
assert_eq!(
None,
checked_read_stdin(&Some("value".to_owned()), &stdin[..])
);
assert_eq!(
Some(b"I'm a test from stdin".to_vec()),
checked_read_stdin(&None, &stdin[..])
);
assert_eq!(
Some(b"I'm a test from stdin".to_vec()),
checked_read_stdin(&Some("-".to_owned()), &stdin[..])
);
assert_eq!(None, checked_read_stdin(&Some("".to_owned()), &stdin[..]));
}
#[test]
fn test_read_binary() {
let x = " 0xff";
assert_eq!(vec![255u8], read_as_binary(&Some(x.to_owned())));
let x = "0xFF";
assert_eq!(vec![255u8], read_as_binary(&Some(x.to_owned())));
let x = " 0xFf";
assert_eq!(vec![255u8], read_as_binary(&Some(x.to_owned())));
let x = " 0xFfx";
assert_eq!(b" 0xFfx".to_vec(), read_as_binary(&Some(x.to_owned())));
let x = " some random data\n\n\0";
assert_eq!(
b" some random data\n\n\0".to_vec(),
read_as_binary(&Some(x.to_owned()))
);
}
}