Skip to content

Commit

Permalink
feature: refactor with a CmdExecutor trait
Browse files Browse the repository at this point in the history
  • Loading branch information
skymacro111666 committed Jun 20, 2024
1 parent 593617d commit d594de5
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 101 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ clap = { version = "4.5.4", features = ["derive"] }
csv = "1.3.0"
derive = "1.0.0"
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
enum_dispatch = "0.3.13"
features = "0.10.0"
rand = "0.8.5"
serde = { version = "1.0.198", features = ["derive"] }
Expand Down
29 changes: 29 additions & 0 deletions src/cli/base64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use std::{fmt, str::FromStr};

use clap::Parser;

use crate::CmdExector;

use super::verify_file;

#[derive(Debug, Parser)]
Expand Down Expand Up @@ -63,3 +65,30 @@ impl fmt::Display for Base64Format {
write!(f, "{}", Into::<&str>::into(*self))
}
}

impl CmdExector for Base64EncodeOpts {
async fn execute(self) -> anyhow::Result<()> {
let mut reader = crate::get_reader(&self.input)?;
let ret = crate::process_encode(&mut reader, self.format)?;
println!("{}", ret);
Ok(())
}
}

impl CmdExector for Base64DecodeOpts {
async fn execute(self) -> anyhow::Result<()> {
let mut reader = crate::get_reader(&self.input)?;
let ret = crate::process_decode(&mut reader, self.format)?;
println!("{}", ret);
Ok(())
}
}

impl CmdExector for Base64SubCommand {
async fn execute(self) -> anyhow::Result<()> {
match self {
Base64SubCommand::Encode(opts) => opts.execute().await,
Base64SubCommand::Decode(opts) => opts.execute().await,
}
}
}
15 changes: 13 additions & 2 deletions src/cli/csv.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::verify_file;
use crate::CmdExector;
use clap::Parser;
use std::{fmt, str::FromStr};

use super::verify_file;

#[derive(Debug, Parser)]
pub struct CsvOpts {
#[arg(short, long, value_parser = verify_file)]
Expand All @@ -23,6 +23,17 @@ pub enum OutputFormat {
Yaml,
}

impl CmdExector for CsvOpts {
async fn execute(self) -> anyhow::Result<()> {
let output: String = if let Some(output) = self.output {
output
} else {
format!("output.{}", self.format)
};
crate::process_csv(&self.input, output, self.format)
}
}

fn parse_format(format: &str) -> Result<OutputFormat, anyhow::Error> {
// match format.to_lowercase().as_str() {
// "json" => Ok(OutputFormat::Json),
Expand Down
20 changes: 20 additions & 0 deletions src/cli/genpass.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::CmdExector;
use clap::Parser;
use zxcvbn::zxcvbn;

#[derive(Debug, Parser)]
pub struct GenPassOpts {
Expand All @@ -13,3 +15,21 @@ pub struct GenPassOpts {
#[arg(short, long, default_value_t = true)]
pub symbol: bool,
}

impl CmdExector for GenPassOpts {
async fn execute(self) -> anyhow::Result<()> {
let ret = crate::process_genpass(
self.length,
self.uppercase,
self.lowercase,
self.number,
self.symbol,
)?;
println!("{}", ret);

// output password strength in stderr
let estimate = zxcvbn(&ret, &[])?;
eprintln!("Password strength: {}", estimate.score());
Ok(())
}
}
16 changes: 16 additions & 0 deletions src/cli/http.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::{process_http_serve, CmdExector};

use super::verify_path;
use clap::Parser;
use std::path::PathBuf;
Expand All @@ -15,3 +17,17 @@ pub struct HttpServeOpts {
#[arg(short, long, default_value_t = 8080)]
pub port: u16,
}

impl CmdExector for HttpServeOpts {
async fn execute(self) -> anyhow::Result<()> {
process_http_serve(self.dir, self.port).await
}
}

impl CmdExector for HttpSubCommand {
async fn execute(self) -> anyhow::Result<()> {
match self {
HttpSubCommand::Serve(opts) => opts.execute().await,
}
}
}
22 changes: 17 additions & 5 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ mod csv;
mod genpass;
mod http;
mod text;
use std::path::{Path, PathBuf};

use crate::CmdExector;
use clap::Parser;
use std::path::{Path, PathBuf};

use self::csv::CsvOpts;
pub use self::{
Expand All @@ -29,14 +29,26 @@ pub enum Subcommand {
Csv(CsvOpts),
#[command(name = "genpass", about = "Generate a random password")]
GenPass(GenPassOpts),
#[command(subcommand)]
#[command(subcommand, about = "Base64 encode/decode")]
Base64(Base64SubCommand),
#[command(subcommand)]
#[command(subcommand, about = "Text sign/verify")]
Text(TextSubCommand),
#[command(subcommand)]
#[command(subcommand, about = "HTTP server")]
Http(HttpSubCommand),
}

impl CmdExector for Subcommand {
async fn execute(self) -> anyhow::Result<()> {
match self {
Subcommand::Csv(opts) => opts.execute().await,
Subcommand::GenPass(opts) => opts.execute().await,
Subcommand::Base64(cmd) => cmd.execute().await,
Subcommand::Text(cmd) => cmd.execute().await,
Subcommand::Http(cmd) => cmd.execute().await,
}
}
}

fn verify_file(filename: &str) -> Result<String, String> {
if filename == "-" || Path::new(filename).exists() {
Ok(filename.into())
Expand Down
52 changes: 52 additions & 0 deletions src/cli/text.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
use super::{verify_file, verify_path};
use crate::{
get_content, get_reader, process_text_key_generate, process_text_sign, process_text_verify,
CmdExector,
};
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use clap::Parser;
use std::{fmt, path::PathBuf, str::FromStr};
use tokio::fs;

#[derive(Debug, Parser)]
pub enum TextSubCommand {
Expand Down Expand Up @@ -79,3 +85,49 @@ impl fmt::Display for TextSignFormat {
write!(f, "{}", Into::<&str>::into(*self))
}
}

impl CmdExector for TextSignOpts {
async fn execute(self) -> anyhow::Result<()> {
let mut reader = get_reader(&self.input)?;
let key = get_content(&self.key)?;
let sig = process_text_sign(&mut reader, &key, self.format)?;
// base64 output
let encoded = URL_SAFE_NO_PAD.encode(sig);
println!("{}", encoded);
Ok(())
}
}
impl CmdExector for TextVerifyOpts {
async fn execute(self) -> anyhow::Result<()> {
let mut reader = get_reader(&self.input)?;
let key = get_content(&self.key)?;
let decoded = URL_SAFE_NO_PAD.decode(&self.sig)?;
let verified = process_text_verify(&mut reader, &key, &decoded, self.format)?;
if verified {
println!("✓ Signature verified");
} else {
println!("⚠ Signature not verified");
}
Ok(())
}
}

impl CmdExector for KeyGenerateOpts {
async fn execute(self) -> anyhow::Result<()> {
let key = process_text_key_generate(self.format)?;
for (k, v) in key {
fs::write(self.output_path.join(k), v).await?;
}
Ok(())
}
}

impl CmdExector for TextSubCommand {
async fn execute(self) -> anyhow::Result<()> {
match self {
TextSubCommand::Sign(opts) => opts.execute().await,
TextSubCommand::Verify(opts) => opts.execute().await,
TextSubCommand::Generate(opts) => opts.execute().await,
}
}
}
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ pub use cli::{
};
pub use process::*;
pub use utils::*;

#[allow(async_fn_in_trait)]
pub trait CmdExector {
async fn execute(self) -> anyhow::Result<()>;
}
96 changes: 2 additions & 94 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,103 +1,11 @@
// use std::process::Output;
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use clap::Parser;
use rcli::{
get_content, get_reader, process_csv, process_decode, process_encode, process_genpass,
process_http_serve, process_text_key_generate, process_text_sign, process_text_verify,
Base64SubCommand, HttpSubCommand, Opts, Subcommand, TextSubCommand,
};
use std::fs;
use zxcvbn::zxcvbn;
use rcli::{CmdExector, Opts};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
let opts = Opts::parse();
// print!("{:?}", opts);

match opts.cmd {
Subcommand::Csv(opts) => {
// let mut reader = Reader::from_path(opts.input)?;
// let records = reader
// .deserialize()
// .map(|record| record.unwrap())
// .collect::<Vec<Player>>();
// print!("{:?} ", records)
// let mut ret: Vec<Player> = Vec::with_capacity(128);
// for result in reader.deserialize() {
// let record: Player = result?;
// // print!("{:?} ", record);
// ret.push(record);
// }
// let json = serde_json::to_string_pretty(&ret)?;
// fs::write(opts.output, json)?;
let output: String = if let Some(output) = opts.output {
output.clone()
} else {
format!("output.{}", opts.format)
};
process_csv(&opts.input, output, opts.format)?;
}
Subcommand::GenPass(opts) => {
let ret = process_genpass(
opts.length,
opts.uppercase,
opts.lowercase,
opts.number,
opts.symbol,
)?;
println!("{}", ret);

// output password strength in stderr
let estimate = zxcvbn(&ret, &[])?;
eprintln!("Password strength: {}", estimate.score());
}
Subcommand::Base64(cmd) => match cmd {
Base64SubCommand::Encode(opts) => {
let mut reader = get_reader(&opts.input)?;
let ret = process_encode(&mut reader, opts.format)?;
println!("{}", ret);
}
Base64SubCommand::Decode(opts) => {
let mut reader = get_reader(&opts.input)?;
let ret = process_decode(&mut reader, opts.format)?;
println!("{}", ret);
}
},
Subcommand::Text(subcmd) => match subcmd {
TextSubCommand::Sign(opts) => {
let mut reader = get_reader(&opts.input)?;
let key = get_content(&opts.key)?;
let sig = process_text_sign(&mut reader, &key, opts.format)?;
// base64 output
let encoded = URL_SAFE_NO_PAD.encode(sig);
println!("{}", encoded);
}
TextSubCommand::Verify(opts) => {
let mut reader = get_reader(&opts.input)?;
let key = get_content(&opts.key)?;
let decoded = URL_SAFE_NO_PAD.decode(&opts.sig)?;
let verified = process_text_verify(&mut reader, &key, &decoded, opts.format)?;
if verified {
println!("✓ Signature verified");
} else {
println!("⚠ Signature not verified");
}
}
TextSubCommand::Generate(opts) => {
let key = process_text_key_generate(opts.format)?;
for (k, v) in key {
fs::write(opts.output_path.join(k), v)?;
}
}
},
Subcommand::Http(cmd) => match cmd {
HttpSubCommand::Serve(opts) => {
// println!("Serving at http://0.0.0.0:{}", opts.port);
process_http_serve(opts.dir, opts.port).await?;
}
},
}
opts.cmd.execute().await?;
Ok(())
}

Expand Down

0 comments on commit d594de5

Please sign in to comment.