Skip to content

Commit

Permalink
feat(parser): Allow people to plug into 'value_parser' macro
Browse files Browse the repository at this point in the history
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
  • Loading branch information
epage committed May 25, 2022
1 parent 408ca3c commit fcdd317
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 88 deletions.
1 change: 1 addition & 0 deletions examples/derive_ref/README.md
Expand Up @@ -175,6 +175,7 @@ These correspond to a `clap::Arg`.
- When not present: case-converted field name is used
- `value_parser [= <expr>]`: `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 = <expr>`: `clap::Arg::help`
- When not present: [Doc comment summary](#doc-comments)
- `long_help = <expr>`: `clap::Arg::long_help`
Expand Down
1 change: 1 addition & 0 deletions src/builder/mod.rs
Expand Up @@ -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;
Expand Down
217 changes: 129 additions & 88 deletions src/builder/value_parser.rs
Expand Up @@ -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<Self::Value, clap::Error> {
/// 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<ValueParser>`. 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<u8>;
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<i8>;
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<u16>;
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<i16>;
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<u32>;
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<i32>;
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<i64>;
fn value_parser() -> Self::Parser {
RangedI64ValueParser::new()
}
}

#[doc(hidden)]
#[derive(Debug)]
pub struct AutoValueParser<T>(std::marker::PhantomData<T>);
Expand All @@ -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<String> {
type Parser = ValueParser;
fn value_parser(&self) -> Self::Parser {
ValueParser::string()
}
}
impl ValueParserViaBuiltIn for &&AutoValueParser<std::ffi::OsString> {
type Parser = ValueParser;
fn value_parser(&self) -> Self::Parser {
ValueParser::os_string()
}
}
impl ValueParserViaBuiltIn for &&AutoValueParser<std::path::PathBuf> {
type Parser = ValueParser;
fn value_parser(&self) -> Self::Parser {
ValueParser::path_buf()
}
}
impl ValueParserViaBuiltIn for &&AutoValueParser<bool> {
type Parser = ValueParser;
fn value_parser(&self) -> Self::Parser {
ValueParser::bool()
}
}
impl ValueParserViaBuiltIn for &&AutoValueParser<u8> {
type Parser = RangedI64ValueParser<u8>;
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<i8> {
type Parser = RangedI64ValueParser<i8>;
impl<P: ValueParserFactory> ValueParserViaFactory for &&AutoValueParser<P> {
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<u16> {
type Parser = RangedI64ValueParser<u16>;
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<i16> {
type Parser = RangedI64ValueParser<i16>;
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<u32> {
type Parser = RangedI64ValueParser<u32>;
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<i32> {
type Parser = RangedI64ValueParser<i32>;
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<i64> {
type Parser = RangedI64ValueParser<i64>;
fn value_parser(&self) -> Self::Parser {
RangedI64ValueParser::new()
P::value_parser()
}
}

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -1784,18 +1835,8 @@ mod private {
pub trait ValueParserViaSelfSealed {}
impl<P: Into<ValueParser>> ValueParserViaSelfSealed for &&&AutoValueParser<P> {}

pub trait ValueParserViaBuiltInSealed {}
impl ValueParserViaBuiltInSealed for &&AutoValueParser<String> {}
impl ValueParserViaBuiltInSealed for &&AutoValueParser<std::ffi::OsString> {}
impl ValueParserViaBuiltInSealed for &&AutoValueParser<std::path::PathBuf> {}
impl ValueParserViaBuiltInSealed for &&AutoValueParser<bool> {}
impl ValueParserViaBuiltInSealed for &&AutoValueParser<u8> {}
impl ValueParserViaBuiltInSealed for &&AutoValueParser<i8> {}
impl ValueParserViaBuiltInSealed for &&AutoValueParser<u16> {}
impl ValueParserViaBuiltInSealed for &&AutoValueParser<i16> {}
impl ValueParserViaBuiltInSealed for &&AutoValueParser<u32> {}
impl ValueParserViaBuiltInSealed for &&AutoValueParser<i32> {}
impl ValueParserViaBuiltInSealed for &&AutoValueParser<i64> {}
pub trait ValueParserViaFactorySealed {}
impl<P: ValueParserFactory> ValueParserViaFactorySealed for &&AutoValueParser<P> {}

pub trait ValueParserViaArgEnumSealed {}
impl<E: crate::ArgEnum> ValueParserViaArgEnumSealed for &AutoValueParser<E> {}
Expand Down

0 comments on commit fcdd317

Please sign in to comment.