From fcdd31781b911f1badc42aa8087361738d00c860 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 25 May 2022 14:12:25 -0500 Subject: [PATCH] feat(parser): Allow people to plug into 'value_parser' macro For most users, this won't be worth doing, they can just specify the parser if needed. Where this has value is crates that integrate custom types into clap, like creating click-like file integration. See https://click.palletsprojects.com/en/8.0.x/arguments/#file-arguments --- examples/derive_ref/README.md | 1 + src/builder/mod.rs | 1 + src/builder/value_parser.rs | 217 ++++++++++++++++++++-------------- 3 files changed, 131 insertions(+), 88 deletions(-) diff --git a/examples/derive_ref/README.md b/examples/derive_ref/README.md index b3e16690222..7beef4d2d24 100644 --- a/examples/derive_ref/README.md +++ b/examples/derive_ref/README.md @@ -175,6 +175,7 @@ These correspond to a `clap::Arg`. - When not present: case-converted field name is used - `value_parser [= ]`: `clap::Arg::value_parser` - When not present: will auto-select an implementation based on the field type + - To register a custom type's `ValueParser`, implement `ValueParserFactory` - `help = `: `clap::Arg::help` - When not present: [Doc comment summary](#doc-comments) - `long_help = `: `clap::Arg::long_help` diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 5cd8e154bcf..9b009ea31fe 100644 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -45,6 +45,7 @@ pub use value_parser::RangedI64ValueParser; pub use value_parser::StringValueParser; pub use value_parser::TypedValueParser; pub use value_parser::ValueParser; +pub use value_parser::ValueParserFactory; #[allow(deprecated)] pub use command::App; diff --git a/src/builder/value_parser.rs b/src/builder/value_parser.rs index 8ba8742f3d2..01aeef57f6f 100644 --- a/src/builder/value_parser.rs +++ b/src/builder/value_parser.rs @@ -1567,6 +1567,127 @@ impl Default for NonEmptyStringValueParser { } } +/// Register a type with [value_parser!][crate::value_parser!] +/// +/// # Example +/// +/// ```rust +/// pub struct Custom(u32); +/// +/// impl clap::builder::ValueParserFactory for Custom { +/// type Parser = CustomValueParser; +/// fn value_parser() -> Self::Parser { +/// CustomValueParser +/// } +/// } +/// +/// pub struct CustomValueParser; +/// impl clap::builder::TypedValueParser for CustomValueParser { +/// type Value = Custom; +/// +/// fn parse_ref( +/// &self, +/// cmd: &clap::Command, +/// arg: Option<&clap::Arg>, +/// value: &std::ffi::OsStr, +/// ) -> Result { +/// let inner = clap::value_parser!(u32); +/// let val = inner.parse_ref(cmd, arg, value)?; +/// Ok(Custom(val)) +/// } +/// } +/// +/// let parser: CustomValueParser = clap::value_parser!(Custom); +/// ``` +pub trait ValueParserFactory { + /// Generated parser, usually [`ValueParser`]. + /// + /// It should at least be a type that supports `Into`. A non-`ValueParser` type + /// allows the caller to do further initialization on the parser. + type Parser; + + /// Create the specified [`Self::Parser`] + fn value_parser() -> Self::Parser; +} +impl ValueParserFactory for String { + type Parser = ValueParser; + fn value_parser() -> Self::Parser { + ValueParser::string() + } +} +impl ValueParserFactory for std::ffi::OsString { + type Parser = ValueParser; + fn value_parser() -> Self::Parser { + ValueParser::os_string() + } +} +impl ValueParserFactory for std::path::PathBuf { + type Parser = ValueParser; + fn value_parser() -> Self::Parser { + ValueParser::path_buf() + } +} +impl ValueParserFactory for bool { + type Parser = ValueParser; + fn value_parser() -> Self::Parser { + ValueParser::bool() + } +} +impl ValueParserFactory for u8 { + type Parser = RangedI64ValueParser; + fn value_parser() -> Self::Parser { + let start: i64 = u8::MIN.into(); + let end: i64 = u8::MAX.into(); + RangedI64ValueParser::new().range(start..=end) + } +} +impl ValueParserFactory for i8 { + type Parser = RangedI64ValueParser; + fn value_parser() -> Self::Parser { + let start: i64 = i8::MIN.into(); + let end: i64 = i8::MAX.into(); + RangedI64ValueParser::new().range(start..=end) + } +} +impl ValueParserFactory for u16 { + type Parser = RangedI64ValueParser; + fn value_parser() -> Self::Parser { + let start: i64 = u16::MIN.into(); + let end: i64 = u16::MAX.into(); + RangedI64ValueParser::new().range(start..=end) + } +} +impl ValueParserFactory for i16 { + type Parser = RangedI64ValueParser; + fn value_parser() -> Self::Parser { + let start: i64 = i16::MIN.into(); + let end: i64 = i16::MAX.into(); + RangedI64ValueParser::new().range(start..=end) + } +} +impl ValueParserFactory for u32 { + type Parser = RangedI64ValueParser; + fn value_parser() -> Self::Parser { + let start: i64 = u32::MIN.into(); + let end: i64 = u32::MAX.into(); + RangedI64ValueParser::new().range(start..=end) + } +} +impl ValueParserFactory for i32 { + type Parser = RangedI64ValueParser; + fn value_parser() -> Self::Parser { + let start: i64 = i32::MIN.into(); + let end: i64 = i32::MAX.into(); + RangedI64ValueParser::new().range(start..=end) + } +} +impl ValueParserFactory for i64 { + type Parser = RangedI64ValueParser; + fn value_parser() -> Self::Parser { + RangedI64ValueParser::new() + } +} + #[doc(hidden)] #[derive(Debug)] pub struct AutoValueParser(std::marker::PhantomData); @@ -1584,86 +1705,14 @@ pub mod via_prelude { use super::*; #[doc(hidden)] - pub trait ValueParserViaBuiltIn: private::ValueParserViaBuiltInSealed { + pub trait ValueParserViaFactory: private::ValueParserViaFactorySealed { type Parser; fn value_parser(&self) -> Self::Parser; } - impl ValueParserViaBuiltIn for &&AutoValueParser { - type Parser = ValueParser; - fn value_parser(&self) -> Self::Parser { - ValueParser::string() - } - } - impl ValueParserViaBuiltIn for &&AutoValueParser { - type Parser = ValueParser; - fn value_parser(&self) -> Self::Parser { - ValueParser::os_string() - } - } - impl ValueParserViaBuiltIn for &&AutoValueParser { - type Parser = ValueParser; - fn value_parser(&self) -> Self::Parser { - ValueParser::path_buf() - } - } - impl ValueParserViaBuiltIn for &&AutoValueParser { - type Parser = ValueParser; - fn value_parser(&self) -> Self::Parser { - ValueParser::bool() - } - } - impl ValueParserViaBuiltIn for &&AutoValueParser { - type Parser = RangedI64ValueParser; - fn value_parser(&self) -> Self::Parser { - let start: i64 = u8::MIN.into(); - let end: i64 = u8::MAX.into(); - RangedI64ValueParser::new().range(start..=end) - } - } - impl ValueParserViaBuiltIn for &&AutoValueParser { - type Parser = RangedI64ValueParser; + impl ValueParserViaFactory for &&AutoValueParser

{ + type Parser = P::Parser; fn value_parser(&self) -> Self::Parser { - let start: i64 = i8::MIN.into(); - let end: i64 = i8::MAX.into(); - RangedI64ValueParser::new().range(start..=end) - } - } - impl ValueParserViaBuiltIn for &&AutoValueParser { - type Parser = RangedI64ValueParser; - fn value_parser(&self) -> Self::Parser { - let start: i64 = u16::MIN.into(); - let end: i64 = u16::MAX.into(); - RangedI64ValueParser::new().range(start..=end) - } - } - impl ValueParserViaBuiltIn for &&AutoValueParser { - type Parser = RangedI64ValueParser; - fn value_parser(&self) -> Self::Parser { - let start: i64 = i16::MIN.into(); - let end: i64 = i16::MAX.into(); - RangedI64ValueParser::new().range(start..=end) - } - } - impl ValueParserViaBuiltIn for &&AutoValueParser { - type Parser = RangedI64ValueParser; - fn value_parser(&self) -> Self::Parser { - let start: i64 = u32::MIN.into(); - let end: i64 = u32::MAX.into(); - RangedI64ValueParser::new().range(start..=end) - } - } - impl ValueParserViaBuiltIn for &&AutoValueParser { - type Parser = RangedI64ValueParser; - fn value_parser(&self) -> Self::Parser { - let start: i64 = i32::MIN.into(); - let end: i64 = i32::MAX.into(); - RangedI64ValueParser::new().range(start..=end) - } - } - impl ValueParserViaBuiltIn for &&AutoValueParser { - type Parser = RangedI64ValueParser; - fn value_parser(&self) -> Self::Parser { - RangedI64ValueParser::new() + P::value_parser() } } @@ -1703,6 +1752,8 @@ pub mod via_prelude { /// Select a [`ValueParser`] implementation from the intended type /// +/// To register a custom type with this macro, implement [`ValueParserFactory`]. +/// /// # Example /// /// Usage: @@ -1784,18 +1835,8 @@ mod private { pub trait ValueParserViaSelfSealed {} impl> ValueParserViaSelfSealed for &&&AutoValueParser

{} - pub trait ValueParserViaBuiltInSealed {} - impl ValueParserViaBuiltInSealed for &&AutoValueParser {} - impl ValueParserViaBuiltInSealed for &&AutoValueParser {} - impl ValueParserViaBuiltInSealed for &&AutoValueParser {} - impl ValueParserViaBuiltInSealed for &&AutoValueParser {} - impl ValueParserViaBuiltInSealed for &&AutoValueParser {} - impl ValueParserViaBuiltInSealed for &&AutoValueParser {} - impl ValueParserViaBuiltInSealed for &&AutoValueParser {} - impl ValueParserViaBuiltInSealed for &&AutoValueParser {} - impl ValueParserViaBuiltInSealed for &&AutoValueParser {} - impl ValueParserViaBuiltInSealed for &&AutoValueParser {} - impl ValueParserViaBuiltInSealed for &&AutoValueParser {} + pub trait ValueParserViaFactorySealed {} + impl ValueParserViaFactorySealed for &&AutoValueParser

{} pub trait ValueParserViaArgEnumSealed {} impl ValueParserViaArgEnumSealed for &AutoValueParser {}