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
This commit is contained in:
Ed Page 2022-05-25 14:12:25 -05:00
parent 408ca3c5d7
commit fcdd31781b
3 changed files with 131 additions and 88 deletions

View file

@ -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`

View file

@ -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;

View file

@ -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>);
@ -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;
impl<P: ValueParserFactory> ValueParserViaFactory for &&AutoValueParser<P> {
type Parser = P::Parser;
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>;
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()
}
}
@ -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<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> {}