@@ -602,9 +602,9 @@ where
602
602
///
603
603
/// As alternatives to implementing `TypedValueParser`,
604
604
/// - Use `Fn(&str) -> Result<T, E>` which implements `TypedValueParser`
605
- /// - [`TypedValueParser::map`] to adapt an existing `TypedValueParser`
605
+ /// - [`TypedValueParser::map`] or [`TypedValueParser::try_map`] to adapt an existing `TypedValueParser`
606
606
///
607
- /// See `ValueParserFactory` for register `TypedValueParser::Value` with
607
+ /// See `ValueParserFactory` to register `TypedValueParser::Value` with
608
608
/// [`value_parser!`][crate::value_parser].
609
609
///
610
610
/// # Example
@@ -726,6 +726,56 @@ pub trait TypedValueParser: Clone + Send + Sync + 'static {
726
726
{
727
727
MapValueParser :: new ( self , func)
728
728
}
729
+
730
+ /// Adapt a `TypedValueParser` from one value to another
731
+ ///
732
+ /// # Example
733
+ ///
734
+ /// ```rust
735
+ /// # use std::ffi::OsString;
736
+ /// # use std::ffi::OsStr;
737
+ /// # use std::path::PathBuf;
738
+ /// # use std::path::Path;
739
+ /// # use clap::Command;
740
+ /// # use clap::Arg;
741
+ /// # use clap::builder::TypedValueParser as _;
742
+ /// # use clap::builder::OsStringValueParser;
743
+ /// let cmd = Command::new("mycmd")
744
+ /// .arg(
745
+ /// Arg::new("flag")
746
+ /// .long("flag")
747
+ /// .value_parser(
748
+ /// OsStringValueParser::new()
749
+ /// .try_map(verify_ext)
750
+ /// )
751
+ /// );
752
+ ///
753
+ /// fn verify_ext(os: OsString) -> Result<PathBuf, &'static str> {
754
+ /// let path = PathBuf::from(os);
755
+ /// if path.extension() != Some(OsStr::new("rs")) {
756
+ /// return Err("only Rust files are supported");
757
+ /// }
758
+ /// Ok(path)
759
+ /// }
760
+ ///
761
+ /// let error = cmd.clone().try_get_matches_from(["mycmd", "--flag", "foo.txt"]).unwrap_err();
762
+ /// error.print();
763
+ ///
764
+ /// let matches = cmd.try_get_matches_from(["mycmd", "--flag", "foo.rs"]).unwrap();
765
+ /// assert!(matches.contains_id("flag"));
766
+ /// assert_eq!(
767
+ /// matches.get_one::<PathBuf>("flag").map(|s| s.as_path()),
768
+ /// Some(Path::new("foo.rs"))
769
+ /// );
770
+ /// ```
771
+ fn try_map < T , E , F > ( self , func : F ) -> TryMapValueParser < Self , F >
772
+ where
773
+ F : Fn ( Self :: Value ) -> Result < T , E > + Clone + Send + Sync + ' static ,
774
+ T : Send + Sync + Clone ,
775
+ E : Into < Box < dyn std:: error:: Error + Send + Sync + ' static > > ,
776
+ {
777
+ TryMapValueParser :: new ( self , func)
778
+ }
729
779
}
730
780
731
781
impl < F , T , E > TypedValueParser for F
@@ -1931,6 +1981,62 @@ where
1931
1981
}
1932
1982
}
1933
1983
1984
+ /// Adapt a `TypedValueParser` from one value to another
1985
+ ///
1986
+ /// See [`TypedValueParser::try_map`]
1987
+ #[ derive( Clone , Debug ) ]
1988
+ pub struct TryMapValueParser < P , F > {
1989
+ parser : P ,
1990
+ func : F ,
1991
+ }
1992
+
1993
+ impl < P , F , T , E > TryMapValueParser < P , F >
1994
+ where
1995
+ P : TypedValueParser ,
1996
+ P :: Value : Send + Sync + Clone ,
1997
+ F : Fn ( P :: Value ) -> Result < T , E > + Clone + Send + Sync + ' static ,
1998
+ T : Send + Sync + Clone ,
1999
+ E : Into < Box < dyn std:: error:: Error + Send + Sync + ' static > > ,
2000
+ {
2001
+ fn new ( parser : P , func : F ) -> Self {
2002
+ Self { parser, func }
2003
+ }
2004
+ }
2005
+
2006
+ impl < P , F , T , E > TypedValueParser for TryMapValueParser < P , F >
2007
+ where
2008
+ P : TypedValueParser ,
2009
+ P :: Value : Send + Sync + Clone ,
2010
+ F : Fn ( P :: Value ) -> Result < T , E > + Clone + Send + Sync + ' static ,
2011
+ T : Send + Sync + Clone ,
2012
+ E : Into < Box < dyn std:: error:: Error + Send + Sync + ' static > > ,
2013
+ {
2014
+ type Value = T ;
2015
+
2016
+ fn parse_ref (
2017
+ & self ,
2018
+ cmd : & crate :: Command ,
2019
+ arg : Option < & crate :: Arg > ,
2020
+ value : & std:: ffi:: OsStr ,
2021
+ ) -> Result < Self :: Value , crate :: Error > {
2022
+ let mid_value = ok ! ( self . parser. parse_ref( cmd, arg, value) ) ;
2023
+ let value = ok ! ( ( self . func) ( mid_value) . map_err( |e| {
2024
+ let arg = arg
2025
+ . map( |a| a. to_string( ) )
2026
+ . unwrap_or_else( || "..." . to_owned( ) ) ;
2027
+ crate :: Error :: value_validation( arg, value. to_string_lossy( ) . into_owned( ) , e. into( ) )
2028
+ . with_cmd( cmd)
2029
+ } ) ) ;
2030
+ Ok ( value)
2031
+ }
2032
+
2033
+ fn possible_values (
2034
+ & self ,
2035
+ ) -> Option < Box < dyn Iterator < Item = crate :: builder:: PossibleValue > + ' _ > > {
2036
+ self . parser . possible_values ( )
2037
+ }
2038
+ }
2039
+
1934
2040
/// Register a type with [value_parser!][crate::value_parser!]
1935
2041
///
1936
2042
/// # Example
0 commit comments