@@ -300,6 +300,9 @@ impl Default for Trim {
300300/// `Option<T>` is deserialized with non-empty but invalid data, then the value
301301/// will be `None` and the error will be ignored.
302302///
303+ /// Use the [`invalid_result`](./fn.invalid_result.html) function if you want to
304+ /// return the invalid values as `Err<String>` instead of discarding them.
305+ ///
303306/// # Example
304307///
305308/// This example shows how to parse CSV records with numerical data, even if
@@ -343,3 +346,97 @@ where
343346{
344347 Option :: < T > :: deserialize ( de) . or_else ( |_| Ok ( None ) )
345348}
349+
350+ /// A custom Serde deserializer for possibly invalid `Result<T, String>` fields.
351+ ///
352+ /// When deserializing CSV data, it is sometimes desirable to return separately
353+ /// fields with invalid data. For example, there might be a field that is
354+ /// usually a number, but will occasionally contain garbage data that causes
355+ /// number parsing to fail.
356+ ///
357+ /// You might be inclined to use, say, `Result<i32, String>` for fields such at
358+ /// this. However this will not compile out of the box, because Serde does not
359+ /// know when to return `Ok<i32>` and when to return `Err<String>`.
360+ ///
361+ /// This function allows you to define the following behavior: if `Result<T,
362+ /// String>` is deserialized with valid data, then the valid value will be
363+ /// returned as `Ok<T>`, while if it is deserialized with empty or invalid data,
364+ /// then the invalid value will be lossily converted to `String` and returned as
365+ /// `Err<String>`.
366+ ///
367+ /// Use the [`invalid_option`](./fn.invalid_option.html) function if you want to
368+ /// discard the invalid values instead of returning them as `Err<String>`.
369+ ///
370+ /// # Example
371+ ///
372+ /// This example shows how to parse CSV records with numerical data, even if
373+ /// some numerical data is absent or invalid. Without the
374+ /// `serde(deserialize_with = "...")` annotations, this example would not
375+ /// compile.
376+ ///
377+ /// ```
378+ /// use std::error::Error;
379+ ///
380+ /// #[derive(Debug, serde::Deserialize, Eq, PartialEq)]
381+ /// struct Row {
382+ /// #[serde(deserialize_with = "csv::invalid_result")]
383+ /// a: Result<i32, String>,
384+ /// #[serde(deserialize_with = "csv::invalid_result")]
385+ /// b: Result<i32, String>,
386+ /// #[serde(deserialize_with = "csv::invalid_result")]
387+ /// c: Result<i32, String>,
388+ /// }
389+ ///
390+ /// # fn main() { example().unwrap(); }
391+ /// fn example() -> Result<(), Box<dyn Error>> {
392+ /// let data = "\
393+ /// a,b,c
394+ /// 5,\"\",xyz
395+ /// ";
396+ /// let mut rdr = csv::Reader::from_reader(data.as_bytes());
397+ /// if let Some(result) = rdr.deserialize().next() {
398+ /// let record: Row = result?;
399+ /// assert_eq!(record, Row { a: Ok(5), b: Err(String::new()), c: Err(String::from("xyz")) });
400+ /// Ok(())
401+ /// } else {
402+ /// Err(From::from("expected at least one record but got none"))
403+ /// }
404+ /// }
405+ /// ```
406+ pub fn invalid_result < ' de , D , T > (
407+ de : D ,
408+ ) -> result:: Result < result:: Result < T , String > , D :: Error >
409+ where
410+ D : Deserializer < ' de > ,
411+ T : Deserialize < ' de > ,
412+ {
413+ let value = serde_value:: Value :: deserialize ( de) ?;
414+ let result = T :: deserialize ( value. clone ( ) ) . map_err ( |_| match value {
415+ serde_value:: Value :: Bool ( b) => b. to_string ( ) ,
416+ serde_value:: Value :: U8 ( u) => u. to_string ( ) ,
417+ serde_value:: Value :: U16 ( u) => u. to_string ( ) ,
418+ serde_value:: Value :: U32 ( u) => u. to_string ( ) ,
419+ serde_value:: Value :: U64 ( u) => u. to_string ( ) ,
420+ serde_value:: Value :: I8 ( i) => i. to_string ( ) ,
421+ serde_value:: Value :: I16 ( i) => i. to_string ( ) ,
422+ serde_value:: Value :: I32 ( i) => i. to_string ( ) ,
423+ serde_value:: Value :: I64 ( i) => i. to_string ( ) ,
424+ serde_value:: Value :: F32 ( f) => f. to_string ( ) ,
425+ serde_value:: Value :: F64 ( f) => f. to_string ( ) ,
426+ serde_value:: Value :: Char ( c) => c. to_string ( ) ,
427+ serde_value:: Value :: String ( s) => s,
428+ serde_value:: Value :: Unit => String :: new ( ) ,
429+ serde_value:: Value :: Option ( option) => {
430+ format ! ( "{:?}" , option)
431+ }
432+ serde_value:: Value :: Newtype ( newtype) => {
433+ format ! ( "{:?}" , newtype)
434+ }
435+ serde_value:: Value :: Seq ( seq) => format ! ( "{:?}" , seq) ,
436+ serde_value:: Value :: Map ( map) => format ! ( "{:?}" , map) ,
437+ serde_value:: Value :: Bytes ( bytes) => {
438+ String :: from_utf8_lossy ( & bytes) . into_owned ( )
439+ }
440+ } ) ;
441+ Ok ( result)
442+ }
0 commit comments