Skip to content

Commit

Permalink
Change CLI to use hash trait.
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelPaddon committed Jun 2, 2024
1 parent f2307d5 commit 3d88a2b
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 62 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ exclude = ["/resources"]
[dependencies]
arrayvec = "0.7.4"
clap = { version = "4.5.4", features = ["derive"] }
derive_more = "0.99.17"
hex = "0.4.3"
num-traits = "0.2.18"
patharg = "0.4.0"
smallvec = { version = "1.13.2", features = ["const_new"] }

[dev-dependencies]
Expand Down
24 changes: 21 additions & 3 deletions src/hash/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
//! Cryptographic hashes.
use std::io::Write;
use derive_more::{Constructor, Display, Error};

#[derive(Clone, Constructor, Debug, Display, Error)]
#[display(fmt = "{}: unknown algorithm", name)]
pub struct UnknownAlgorithmError {
name: String
}

/// A cryptographic hash algorithm.
pub trait Hash {
pub trait Hash: Write {
/// Constructs a new hash algorithm instance.
fn new() -> Self
where
Expand Down Expand Up @@ -91,6 +100,8 @@ macro_rules! impl_hash_newtype {
}
}

pub mod sha2;

const ALGORITHMS: [(&str, fn() -> Box<dyn Hash>); 6] = [
("sha224", || Box::new(sha2::Sha224::new())),
("sha256", || Box::new(sha2::Sha256::new())),
Expand All @@ -100,8 +111,15 @@ const ALGORITHMS: [(&str, fn() -> Box<dyn Hash>); 6] = [
("sha512_256", || Box::new(sha2::Sha512_256::new()))
];

pub fn algorithms() -> impl Iterator {
pub fn algorithms() -> impl Iterator<Item = &'static str> {
ALGORITHMS.iter().map(|x| x.0)
}

pub mod sha2;
pub fn from_string(name: &str)
-> Result<Box<dyn Hash>, UnknownAlgorithmError>
{
match ALGORITHMS.binary_search_by(|x| x.0.cmp(name)) {
Ok(i) => Ok(ALGORITHMS[i].1()),
_ => Err(UnknownAlgorithmError::new(name.to_string()))
}
}
107 changes: 48 additions & 59 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,89 +1,78 @@
use clap::{Parser, Subcommand};
use clap::{Args, Parser, Subcommand};
use patharg::{InputArg, OutputArg};
use std::error::Error;
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Write, copy, stdin, stdout};
use std::io;
use std::io::Write;
use std::path::PathBuf;
use scytale::hash::Hash;
use scytale::hash::sha2::{
Sha224, Sha256, Sha384, Sha512, Sha512_224, Sha512_256
};
use scytale::hash;

#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Command
}

#[derive(Args)]
struct InputOption {
/// input file (defaults to stdin)
#[arg(short, long)]
input: Option<PathBuf>,
}

#[derive(Args)]
struct OutputOption {
/// output file (defaults to stdout)
#[arg(short, long)]
output: Option<PathBuf>,

#[command(subcommand)]
command: Option<Command>
}

#[derive(Subcommand)]
enum Command {
/// compute a SHA2-224 digest
Sha224,

/// compute a SHA2-256 digest
Sha256,
/// compute a hash
Hash {
#[command(flatten)]
input: InputOption,

/// compute a SHA2-384 digest
Sha384,
#[command(flatten)]
output: OutputOption,

/// compute a SHA2-512 digest
Sha512,

/// compute a SHA2-512/224 digest
Sha512_224,

/// compute a SHA2-512/256 digest
Sha512_256,
/// name of hash algorithm
algorithm: Option<String>
}
}

fn main() -> Result<(), Box<dyn Error>> {
let cli = Cli::parse();
match cli.command {
Some(Command::Sha224) =>
do_hash::<Sha224>(cli.input, cli.output),
Some(Command::Sha256) =>
do_hash::<Sha256>(cli.input, cli.output),
Some(Command::Sha384) =>
do_hash::<Sha384>(cli.input, cli.output),
Some(Command::Sha512) =>
do_hash::<Sha512>(cli.input, cli.output),
Some(Command::Sha512_224) =>
do_hash::<Sha512_224>(cli.input, cli.output),
Some(Command::Sha512_256) =>
do_hash::<Sha512_256>(cli.input, cli.output),
None => Ok(())
Command::Hash{input, output, algorithm}
=> hash_command(input.input, output.output, algorithm),
}
}

fn do_hash<H: Hash + Write>(inpath: Option<PathBuf>, outpath: Option<PathBuf>)
-> Result<(), Box<dyn Error>>
{
let mut input: BufReader<Box<dyn Read>> = BufReader::new(
match inpath {
Some(path) => Box::new(File::open(path)?),
None => Box::new(stdin())
}
);

let mut output: BufWriter<Box<dyn Write>> = BufWriter::new(
match outpath {
Some(path) => Box::new(File::open(path)?),
None => Box::new(stdout())
}
);

let mut hash = H::new();
copy(&mut input, &mut hash)?;
let digest = hash.finalize();
writeln!(&mut output, "{}", hex::encode(digest.as_ref()))?;
fn hash_command(
inpath: Option<PathBuf>,
outpath: Option<PathBuf>,
algorithm: Option<String>
) -> Result<(), Box<dyn Error>> {
let input = inpath.map_or(InputArg::default(),
|x| InputArg::from_arg(x));
let output = outpath.map_or(OutputArg::default(),
|x| OutputArg::from_arg(x));

match algorithm {
Some(name) => {
let mut h = hash::from_string(&name)?;
let mut reader = input.open()?;
io::copy(&mut reader, &mut h)?;
let mut writer = output.create()?;
writeln!(&mut writer, "{}", hex::encode(h.finalize()))?;
},
None => {
for name in hash::algorithms() {
println!("{}", name)
}
}
};
Ok(())
}

0 comments on commit 3d88a2b

Please sign in to comment.