@@ -300,6 +300,9 @@ impl Default for Trim {
300
300
/// `Option<T>` is deserialized with non-empty but invalid data, then the value
301
301
/// will be `None` and the error will be ignored.
302
302
///
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
+ ///
303
306
/// # Example
304
307
///
305
308
/// This example shows how to parse CSV records with numerical data, even if
@@ -343,3 +346,97 @@ where
343
346
{
344
347
Option :: < T > :: deserialize ( de) . or_else ( |_| Ok ( None ) )
345
348
}
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