@@ -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,98 @@ 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 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