diff --git a/examples/compiler.rs b/examples/compiler.rs index f2fe47b..5d067f1 100644 --- a/examples/compiler.rs +++ b/examples/compiler.rs @@ -1,6 +1,6 @@ use badargs::arg; -arg!(OutFile: "output", 'o' -> Option); +arg!(OutFile: "output", 'o' -> String, required); arg!(Force: "force", 'f' -> bool); arg!(OLevel: "optimize" -> usize); diff --git a/src/lib.rs b/src/lib.rs index 2ad6317..06e5daf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,9 @@ where /// /// ``` /// # use badargs::arg; -/// arg!(OutFile: "output", 'o' -> Option); +/// arg!(Force: "force", 'f' -> bool); +/// +/// arg!(OutFile: "output", 'o' -> String, required); /// // OutFile now implements CliArg /// ``` // This trait requires any because some dynamic typing is done in the background @@ -40,6 +42,7 @@ pub trait CliArg: Any { fn long() -> &'static str; fn short() -> Option; + fn required() -> bool; } /// The struct containing parsed argument information @@ -79,7 +82,6 @@ macro_rules! impl_cli_return { impl_cli_return!( for String => String; - for Option => OptionString; for bool => Bool; for isize => INum; for usize => UNum @@ -90,7 +92,7 @@ mod sealed { macro_rules! impl_ { ($($name:ty),+) => {$(impl SealedCliReturnValue for $name{})+}; } - impl_!(String, Option, bool, usize, isize); + impl_!(String, bool, usize, isize); } mod error { diff --git a/src/macros.rs b/src/macros.rs index 8690981..364fefb 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,12 +1,20 @@ #[macro_export] macro_rules! arg { + // implicit optional ($name:ident: $long:literal, $short:literal -> $result:ty) => { - arg!(@$name: ($long, ::std::option::Option::Some($short)) -> $result); + arg!(@$name: ($long, ::std::option::Option::Some($short)) -> $result, false); }; ($name:ident: $long:literal -> $result:ty) => { - arg!(@$name: ($long, ::std::option::Option::None) -> $result); + arg!(@$name: ($long, ::std::option::Option::None) -> $result, false); }; - (@$name:ident: ($long:literal, $short:expr) -> $result:ty) => { + // required + ($name:ident: $long:literal, $short:literal -> $result:ty, required) => { + arg!(@$name: ($long, ::std::option::Option::Some($short)) -> $result, true); + }; + ($name:ident: $long:literal -> $result:ty, required) => { + arg!(@$name: ($long, ::std::option::Option::None) -> $result, true); + }; + (@$name:ident: ($long:literal, $short:expr) -> $result:ty, $required:literal) => { #[derive(Default)] struct $name; @@ -20,6 +28,10 @@ macro_rules! arg { fn short() -> Option { $short } + + fn required() -> bool { + $required + } } }; } diff --git a/src/parse.rs b/src/parse.rs index 23faa2b..d87e414 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,5 +1,5 @@ use crate::error::CallError; -use crate::schema::{Schema, SchemaKind, SchemaKindType}; +use crate::schema::{Schema, SchemaKind}; use std::any::Any; use std::collections::HashMap; use std::iter::Peekable; @@ -60,13 +60,8 @@ fn parse_shorts( .short(flag) .ok_or_else(|| CallError::ShortFlagNotFound(flag))?; - let inner_kind = match command.kind { - SchemaKind::Required(inner) => inner, - SchemaKind::Optional(inner) => inner, - }; - - match inner_kind { - SchemaKindType::String => { + match command.kind { + SchemaKind::String => { let next = args .next() .ok_or_else(|| CallError::ExpectedValue(command.long.to_string()))?; diff --git a/src/schema.rs b/src/schema.rs index c80dbc3..ff36728 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -7,18 +7,12 @@ use super::Result; use crate::{CliArg, CliReturnValue, SchemaError}; use std::collections::HashMap; -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum SchemaKind { - Required(SchemaKindType), - Optional(SchemaKindType), -} - /// /// The type of value the argument returns /// /// This could *maybe* also be solved with trait objects but lets keep this for now #[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum SchemaKindType { +pub enum SchemaKind { String, Bool, INum, @@ -32,6 +26,7 @@ pub struct SchemaCommand { pub kind: SchemaKind, pub long: &'static str, pub short: Option, + pub required: bool, } /// @@ -107,14 +102,17 @@ where T: CliArg, { fn add_schema(schema: &mut Schema) -> Result<()> { - let kind = T::Content::kind(); - let long = T::long(); let short = T::short(); - let command = SchemaCommand { kind, long, short }; - if let Some(short_name) = short { - schema.add_short_command(short_name, command)?; + let command = SchemaCommand { + kind: T::Content::kind(), + long: T::long(), + short, + required: T::required(), + }; + if let Some(short) = short { + schema.add_short_command(short, command)?; } - schema.add_command(long, command) + schema.add_command(T::long(), command) } } @@ -132,9 +130,10 @@ mod test { fn one_command_schema() { let schema = Schema::create::().unwrap(); let out_file = SchemaCommand { - kind: SchemaKind::Optional(SchemaKindType::String), + kind: SchemaKind::String, long: "output", short: Some('o'), + required: false, }; assert_eq!(schema.longs.get("output"), Some(&out_file)); assert_eq!(schema.shorts.get(&'o'), Some(&out_file)); @@ -145,14 +144,16 @@ mod test { fn two_command_schema() { let schema = Schema::create::<(OutFile, Force)>().unwrap(); let out_file = SchemaCommand { - kind: SchemaKind::Optional(SchemaKindType::String), + kind: SchemaKind::String, long: "output", short: Some('o'), + required: false, }; let force = SchemaCommand { - kind: SchemaKind::Required(SchemaKindType::Bool), + kind: SchemaKind::Bool, long: "force", short: Some('f'), + required: true, }; assert_eq!(schema.longs.get("output"), Some(&out_file)); @@ -168,19 +169,22 @@ mod test { fn three_command_schema() { let schema = Schema::create::<(OutFile, (Force, SetUpstream))>().unwrap(); let out_file = SchemaCommand { - kind: SchemaKind::Optional(SchemaKindType::String), + kind: SchemaKind::String, long: "output", short: Some('o'), + required: false, }; let force = SchemaCommand { - kind: SchemaKind::Required(SchemaKindType::Bool), + kind: SchemaKind::Bool, long: "force", short: Some('f'), + required: true, }; let set_upstream = SchemaCommand { - kind: SchemaKind::Required(SchemaKindType::String), + kind: SchemaKind::String, long: "set-upstream", short: None, + required: true, }; assert_eq!(schema.longs.get("output"), Some(&out_file));