keyfork-bin: initial commit
This commit is contained in:
parent
d481c7e164
commit
ed61d0685a
|
@ -1674,6 +1674,7 @@ dependencies = [
|
|||
"card-backend-pcsc",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"keyfork-bin",
|
||||
"keyfork-derive-openpgp",
|
||||
"keyfork-derive-util",
|
||||
"keyfork-entropy",
|
||||
|
@ -1692,6 +1693,13 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keyfork-bin"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keyfork-crossterm"
|
||||
version = "0.27.1"
|
||||
|
|
|
@ -14,6 +14,7 @@ members = [
|
|||
"crates/qrcode/keyfork-qrcode",
|
||||
"crates/qrcode/keyfork-zbar",
|
||||
"crates/qrcode/keyfork-zbar-sys",
|
||||
"crates/util/keyfork-bin",
|
||||
"crates/util/keyfork-crossterm",
|
||||
"crates/util/keyfork-entropy",
|
||||
"crates/util/keyfork-frame",
|
||||
|
|
|
@ -43,3 +43,4 @@ openpgp-card-sequoia = { version = "0.2.0", default-features = false }
|
|||
openpgp-card = "0.4.1"
|
||||
clap_complete = { version = "4.4.6", optional = true }
|
||||
sequoia-openpgp = { version = "1.17.0", default-features = false, features = ["compression"] }
|
||||
keyfork-bin = { version = "0.1.0", path = "../util/keyfork-bin" }
|
||||
|
|
|
@ -6,21 +6,16 @@ use std::process::ExitCode;
|
|||
|
||||
use clap::Parser;
|
||||
|
||||
use keyfork_bin::{Bin, ClosureBin};
|
||||
|
||||
mod cli;
|
||||
mod config;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let bin = ClosureBin::new(|| {
|
||||
let opts = cli::Keyfork::parse();
|
||||
opts.command.handle(&opts)
|
||||
});
|
||||
|
||||
if let Err(e) = opts.command.handle(&opts) {
|
||||
eprintln!("Unable to run command: {e}");
|
||||
let mut source = e.source();
|
||||
while let Some(new_error) = source.take() {
|
||||
eprintln!("Source: {new_error}");
|
||||
source = new_error.source();
|
||||
}
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
ExitCode::SUCCESS
|
||||
bin.main()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "keyfork-bin"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.79"
|
|
@ -0,0 +1,140 @@
|
|||
#![allow(clippy::needless_doctest_main)]
|
||||
|
||||
//! A convenient trait for quickly writing binaries in a consistent pattern.
|
||||
//!
|
||||
//! # Examples
|
||||
//! ```rust
|
||||
//! use anyhow::anyhow;
|
||||
//! use keyfork_bin::Bin;
|
||||
//!
|
||||
//! struct Main;
|
||||
//!
|
||||
//! impl Bin for Main {
|
||||
//! type Args = (String, String);
|
||||
//!
|
||||
//! fn usage_hint(&self) -> Option<String> {
|
||||
//! Some(String::from("<param1> <param2>"))
|
||||
//! }
|
||||
//!
|
||||
//! fn validate_args(&self, mut args: impl Iterator<Item = String>) -> keyfork_bin::ProcessResult<Self::Args> {
|
||||
//! let arg1 = args.next().ok_or(anyhow!("missing argument 1"))?;
|
||||
//! let arg2 = args.next().ok_or(anyhow!("missing argument 2"))?;
|
||||
//! Ok((arg1, arg2))
|
||||
//! }
|
||||
//!
|
||||
//! fn run(&self, (arg1, arg2): Self::Args) -> keyfork_bin::ProcessResult {
|
||||
//! println!("First argument: {arg1}");
|
||||
//! println!("Second argument: {arg2}");
|
||||
//! Ok(())
|
||||
//! }
|
||||
//!#
|
||||
//!# fn main(&self) -> std::process::ExitCode {
|
||||
//!# self.main_inner([String::from("hello"), String::from("world")].into_iter())
|
||||
//!# }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! // Assume the program was called with something like "hello world"...
|
||||
//! let bin = Main;
|
||||
//! bin.main();
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
/// A result that may contain any error.
|
||||
pub type ProcessResult<T = ()> = Result<T, Box<dyn std::error::Error>>;
|
||||
|
||||
fn report_err(e: Box<dyn std::error::Error>) {
|
||||
eprintln!("Unable to run command: {e}");
|
||||
let mut source = e.source();
|
||||
while let Some(new_error) = source.take() {
|
||||
eprintln!("- Caused by: {new_error}");
|
||||
source = new_error.source();
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for implementing the flow of a binary's execution.
|
||||
pub trait Bin {
|
||||
/// The type for command-line arguments required by the function.
|
||||
type Args;
|
||||
|
||||
/// A usage hint for how the arguments should be provided to the program.
|
||||
fn usage_hint(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Validate the arguments provided by the user into types required by the binary.
|
||||
#[allow(clippy::missing_errors_doc)]
|
||||
fn validate_args(&self, args: impl Iterator<Item = String>) -> ProcessResult<Self::Args>;
|
||||
|
||||
/// Run the binary
|
||||
#[allow(clippy::missing_errors_doc)]
|
||||
fn run(&self, args: Self::Args) -> ProcessResult;
|
||||
|
||||
/// The default handler for running the binary and reporting any errors.
|
||||
fn main(&self) -> ExitCode {
|
||||
self.main_inner(std::env::args())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn main_inner(&self, mut args: impl Iterator<Item = String>) -> ExitCode {
|
||||
let command = args.next();
|
||||
let args = match self.validate_args(args) {
|
||||
Ok(args) => args,
|
||||
Err(e) => {
|
||||
if let (Some(command), Some(hint)) = (command, self.usage_hint()) {
|
||||
eprintln!("Usage: {command} {hint}");
|
||||
}
|
||||
report_err(e);
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = self.run(args) {
|
||||
report_err(e);
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
/// A Bin that doesn't take any arguments.
|
||||
pub struct ClosureBin<F: Fn() -> ProcessResult> {
|
||||
closure: F
|
||||
}
|
||||
|
||||
impl<F> ClosureBin<F> where F: Fn() -> ProcessResult {
|
||||
/// Create a new Bin from a closure.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use keyfork_bin::{Bin, ClosureBin};
|
||||
///
|
||||
/// let bin = ClosureBin::new(|| {
|
||||
/// println!("Hello, world!");
|
||||
/// Ok(())
|
||||
/// });
|
||||
///
|
||||
/// bin.main();
|
||||
/// ```
|
||||
pub fn new(closure: F) -> Self {
|
||||
Self {
|
||||
closure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Bin for ClosureBin<F> where F: Fn() -> ProcessResult {
|
||||
type Args = ();
|
||||
|
||||
fn validate_args(&self, _args: impl Iterator<Item = String>) -> ProcessResult<Self::Args> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, _args: Self::Args) -> ProcessResult {
|
||||
let c = &self.closure;
|
||||
c()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue