@@ -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,98 @@ 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 converted to `String` and returned as
365+ /// `Err<String>`. Note that any invalid UTF-8 bytes are lossily converted to
366+ /// `String`, therefore this function will never fail.
367+ ///
368+ /// Use the [`invalid_option`](./fn.invalid_option.html) function if you want to
369+ /// discard the invalid values instead of returning them as `Err<String>`.
370+ ///
371+ /// # Example
372+ ///
373+ /// This example shows how to parse CSV records with numerical data, even if
374+ /// some numerical data is absent or invalid. Without the
375+ /// `serde(deserialize_with = "...")` annotations, this example would not
376+ /// compile.
377+ ///
378+ /// ```
379+ /// use std::error::Error;
380+ ///
381+ /// #[derive(Debug, serde::Deserialize, Eq, PartialEq)]
382+ /// struct Row {
383+ /// #[serde(deserialize_with = "csv::invalid_result")]
384+ /// a: Result<i32, String>,
385+ /// #[serde(deserialize_with = "csv::invalid_result")]
386+ /// b: Result<i32, String>,
387+ /// #[serde(deserialize_with = "csv::invalid_result")]
388+ /// c: Result<i32, String>,
389+ /// }
390+ ///
391+ /// # fn main() { example().unwrap(); }
392+ /// fn example() -> Result<(), Box<dyn Error>> {
393+ /// let data = "\
394+ /// a,b,c
395+ /// 5,\"\",xyz
396+ /// ";
397+ /// let mut rdr = csv::Reader::from_reader(data.as_bytes());
398+ /// if let Some(result) = rdr.deserialize().next() {
399+ /// let record: Row = result?;
400+ /// assert_eq!(record, Row { a: Ok(5), b: Err(String::new()), c: Err(String::from("xyz")) });
401+ /// Ok(())
402+ /// } else {
403+ /// Err(From::from("expected at least one record but got none"))
404+ /// }
405+ /// }
406+ /// ```
407+ pub fn invalid_result < ' de , D , T > (
408+ de : D ,
409+ ) -> result:: Result < result:: Result < T , String > , D :: Error >
410+ where
411+ D : Deserializer < ' de > ,
412+ T : Deserialize < ' de > ,
413+ {
414+ let value = serde_value:: Value :: deserialize ( de) ?;
415+ let result = T :: deserialize ( value. clone ( ) ) . map_err ( |_| match value {
416+ serde_value:: Value :: Bool ( b) => b. to_string ( ) ,
417+ serde_value:: Value :: U8 ( u) => u. to_string ( ) ,
418+ serde_value:: Value :: U16 ( u) => u. to_string ( ) ,
419+ serde_value:: Value :: U32 ( u) => u. to_string ( ) ,
420+ serde_value:: Value :: U64 ( u) => u. to_string ( ) ,
421+ serde_value:: Value :: I8 ( i) => i. to_string ( ) ,
422+ serde_value:: Value :: I16 ( i) => i. to_string ( ) ,
423+ serde_value:: Value :: I32 ( i) => i. to_string ( ) ,
424+ serde_value:: Value :: I64 ( i) => i. to_string ( ) ,
425+ serde_value:: Value :: F32 ( f) => f. to_string ( ) ,
426+ serde_value:: Value :: F64 ( f) => f. to_string ( ) ,
427+ serde_value:: Value :: Char ( c) => c. to_string ( ) ,
428+ serde_value:: Value :: String ( s) => s,
429+ serde_value:: Value :: Unit => String :: new ( ) ,
430+ serde_value:: Value :: Option ( option) => {
431+ format ! ( "{:?}" , option)
432+ }
433+ serde_value:: Value :: Newtype ( newtype) => {
434+ format ! ( "{:?}" , newtype)
435+ }
436+ serde_value:: Value :: Seq ( seq) => format ! ( "{:?}" , seq) ,
437+ serde_value:: Value :: Map ( map) => format ! ( "{:?}" , map) ,
438+ serde_value:: Value :: Bytes ( bytes) => {
439+ String :: from_utf8_lossy ( & bytes) . into_owned ( )
440+ }
441+ } ) ;
442+ Ok ( result)
443+ }
0 commit comments