keyfork-bin: initial commit
This commit is contained in:
parent
d481c7e164
commit
ed61d0685a
|
@ -1674,6 +1674,7 @@ dependencies = [
|
||||||
"card-backend-pcsc",
|
"card-backend-pcsc",
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
|
"keyfork-bin",
|
||||||
"keyfork-derive-openpgp",
|
"keyfork-derive-openpgp",
|
||||||
"keyfork-derive-util",
|
"keyfork-derive-util",
|
||||||
"keyfork-entropy",
|
"keyfork-entropy",
|
||||||
|
@ -1692,6 +1693,13 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "keyfork-bin"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyfork-crossterm"
|
name = "keyfork-crossterm"
|
||||||
version = "0.27.1"
|
version = "0.27.1"
|
||||||
|
|
|
@ -14,6 +14,7 @@ members = [
|
||||||
"crates/qrcode/keyfork-qrcode",
|
"crates/qrcode/keyfork-qrcode",
|
||||||
"crates/qrcode/keyfork-zbar",
|
"crates/qrcode/keyfork-zbar",
|
||||||
"crates/qrcode/keyfork-zbar-sys",
|
"crates/qrcode/keyfork-zbar-sys",
|
||||||
|
"crates/util/keyfork-bin",
|
||||||
"crates/util/keyfork-crossterm",
|
"crates/util/keyfork-crossterm",
|
||||||
"crates/util/keyfork-entropy",
|
"crates/util/keyfork-entropy",
|
||||||
"crates/util/keyfork-frame",
|
"crates/util/keyfork-frame",
|
||||||
|
|
|
@ -43,3 +43,4 @@ openpgp-card-sequoia = { version = "0.2.0", default-features = false }
|
||||||
openpgp-card = "0.4.1"
|
openpgp-card = "0.4.1"
|
||||||
clap_complete = { version = "4.4.6", optional = true }
|
clap_complete = { version = "4.4.6", optional = true }
|
||||||
sequoia-openpgp = { version = "1.17.0", default-features = false, features = ["compression"] }
|
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 clap::Parser;
|
||||||
|
|
||||||
|
use keyfork_bin::{Bin, ClosureBin};
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
let opts = cli::Keyfork::parse();
|
let bin = ClosureBin::new(|| {
|
||||||
|
let opts = cli::Keyfork::parse();
|
||||||
|
opts.command.handle(&opts)
|
||||||
|
});
|
||||||
|
|
||||||
if let Err(e) = opts.command.handle(&opts) {
|
bin.main()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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