From d2788c133ceac446fa1904d9bd04fe2572e62248 Mon Sep 17 00:00:00 2001 From: Nilstrieb Date: Fri, 1 Oct 2021 23:24:26 +0200 Subject: [PATCH] documentation and tests --- examples/compiler.rs | 2 +- src/lib.rs | 55 ++++++++++++++++++++++++++++--------- src/macros.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 14 deletions(-) diff --git a/examples/compiler.rs b/examples/compiler.rs index be32f5a..c4ebfd0 100644 --- a/examples/compiler.rs +++ b/examples/compiler.rs @@ -5,7 +5,7 @@ arg!(Force: "force", 'f' -> bool); arg!(OLevel: "optimize" -> usize); fn main() { - let args = badargs::badargs::<(OutFile, (Force, OLevel))>().unwrap(); + let args = badargs::badargs!(OutFile, Force, OLevel); let _outfile = args.get::(); let _force = args.get::(); diff --git a/src/lib.rs b/src/lib.rs index dd5b192..849e839 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,35 @@ +//! +//! # badargs +//! +//! A fully type-safe argument parser without any proc macros! +//! +//! Declare your arguments with structs. You probably want to use the macro for that +//! ``` +//! # use badargs::arg; +//! arg!(Force: "force", 'f' -> bool); +//! arg!(OutFile: "output", 'o' -> String); +//! ``` +//! Then you call the [`badargs`] function with all of your declared arguments. You probably +//! want to use a macro for that too. +//! +//! You can also use the [`badargs!`] macro if you have many arguments and don't want to nest +//! the tuples manually +//! ``` +//! # use badargs::arg; +//! # arg!(Force: "force", 'f' -> bool); +//! # arg!(OutFile: "output", 'o' -> String); +//! let args = badargs::badargs!(Force, OutFile); +//! ``` +//! You can then get values using your declared arguments +//! ``` +//! # use badargs::arg; +//! # arg!(Force: "force", 'f' -> bool); +//! # arg!(OutFile: "output", 'o' -> String); +//! # let args = badargs::badargs!(Force, OutFile); +//! let force: Option<&bool> = args.get::(); +//! let out_file: Option<&String> = args.get::(); +//! ``` + mod macros; mod parse; mod schema; @@ -13,15 +45,20 @@ pub type Result = std::result::Result; /// /// Parses the command line arguments based on the provided schema S -pub fn badargs() -> Result +/// +/// # Panics +/// +/// This function panics if an invalid schema is entered +/// +pub fn badargs() -> BadArgs where S: IntoSchema, { - let arg_schema = Schema::create::()?; + let arg_schema = Schema::create::().expect("Invalid schema!"); let args = CliArgs::from_args(&arg_schema, std::env::args()).expect("todo"); - Ok(BadArgs { args }) + BadArgs { args } } /// @@ -29,12 +66,6 @@ where /// /// This is mostly done using unit structs and the `arg!` macro /// -/// ``` -/// # use badargs::arg; -/// arg!(Force: "force", 'f' -> bool); -/// arg!(OutFile: "output", 'o' -> String); -/// // OutFile now implements CliArg -/// ``` // This trait requires any because some dynamic typing is done in the background pub trait CliArg: Any { type Content: CliReturnValue; @@ -51,14 +82,12 @@ pub struct BadArgs { impl BadArgs { /// Get the content of an argument by providing the type of the argument - pub fn get(&self) -> &T::Content + pub fn get(&self) -> Option<&T::Content> where T: CliArg, { let long_name = T::long(); - self.args - .get::(long_name) - .expect("it has been validated") + self.args.get::(long_name) } } diff --git a/src/macros.rs b/src/macros.rs index e7383e1..560019e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,3 +1,26 @@ +/// +/// Declare your arguments using this macro. +/// ``` +/// # use badargs::arg; +/// arg!(Force: "force", 'f' -> bool); +/// ``` +/// is a shorthand for +/// ``` +/// # use badargs::{arg, CliArg}; +/// struct Force; +/// +/// impl CliArg for Force { +/// type Content = bool; +/// +/// fn long() -> &'static str { +/// "force" +/// } +/// +/// fn short() -> Option { +/// Some('f') +/// } +/// } +/// ``` #[macro_export] macro_rules! arg { // implicit optional @@ -24,3 +47,45 @@ macro_rules! arg { } }; } + +/// +/// A shorthand for calling the [`badargs::badargs`] main function +/// This macro lets you specify your arguments in a flat list, and then converts them into +/// nested tuples for you, since that's what's internally used. +/// ``` +/// # use badargs::arg; +/// # arg!(Force: "force", 'f' -> bool); +/// # arg!(OutFile: "outfile", 't' -> bool); +/// # arg!(SetUpstream: "set-upstream", 'x' -> bool); +/// # fn main() { +/// let args = badargs::badargs!(Force, OutFile, SetUpstream); +/// # } +/// ``` +/// will be expanded into +/// ``` +/// # use badargs::arg; +/// # arg!(Force: "force", 'f' -> bool); +/// # arg!(OutFile: "outfile", 't' -> bool); +/// # arg!(SetUpstream: "set-upstream", 'x' -> bool); +/// let args = badargs::badargs::<(Force, (OutFile, SetUpstream))>(); +/// ``` +/// This only provides a minor benefit for programs with a small amount of args, but is +/// very useful for larger arg amounts. +#[macro_export] +macro_rules! badargs { + (@inner $head:ty) => { + $head + }; + (@inner $head:ty, $($tail:ty),+) => { + ($head, $crate::badargs!(@inner $($tail),+)) + }; + ($($tail:ty),+) => { + { + #[allow(unused_parens)] // allow this because there might only be one arg + { + + $crate::badargs::<($crate::badargs!(@inner $($tail),+))>() + } + } + }; +}