Skip to content

Commit d901981

Browse files
bors[bot]fionawhim
andauthored
Merge #13
13: Deserialization of tuple structs and IgnoredAny r=japaric a=fionawhim The derived Deserialize implementation expects to be able to generate tuple structs from arrays, so this implements deserialize_tuple_struct to delegate to deserialize_seq. Also implements deserialize_ignored_any so that Deserializers can ignore fields of objects that they don’t care about. Includes some comments explaining why certain deserialize methods are unimplemented (I chose to use comments rather than passing a string to unreachable! so that the strings wouldn’t end up in the binary). Also adds a "CustomError" enum so that errors generated from the derive macro don’t cause a panic. (And includes a note that maybe we could put a heapless String there.) Co-authored-by: Fin Hopkins <fin@draughtleaders.com>
2 parents b58fad5 + bf5533a commit d901981

File tree

2 files changed

+111
-7
lines changed

2 files changed

+111
-7
lines changed

src/de/map.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,12 @@ impl<'de, 'a> de::Deserializer<'de> for MapKey<'a, 'de> {
283283
self.deserialize_str(visitor)
284284
}
285285

286-
fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
286+
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
287287
where
288288
V: Visitor<'de>,
289289
{
290-
unreachable!()
290+
// Even if we’re ignoring the contents of the map, we still need to
291+
// deserialize the string here in order to chomp the key’s characters.
292+
self.deserialize_str(visitor)
291293
}
292294
}

src/de/mod.rs

Lines changed: 107 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ pub enum Error {
6767
/// JSON has a comma after the last value in an array or map.
6868
TrailingComma,
6969

70+
/// Error with a custom message that we had to discard.
71+
CustomError,
72+
7073
#[doc(hidden)]
7174
__Extensible,
7275
}
@@ -303,6 +306,7 @@ macro_rules! deserialize_fromstr {
303306
impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
304307
type Error = Error;
305308

309+
/// Unsupported. Can’t parse a value without knowing its expected type.
306310
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
307311
where
308312
V: Visitor<'de>,
@@ -425,20 +429,23 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
425429
}
426430
}
427431

432+
/// Unsupported. String is not available in no-std.
428433
fn deserialize_string<V>(self, _visitor: V) -> Result<V::Value>
429434
where
430435
V: Visitor<'de>,
431436
{
432437
unreachable!()
433438
}
434439

440+
/// Unsupported
435441
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
436442
where
437443
V: Visitor<'de>,
438444
{
439445
unreachable!()
440446
}
441447

448+
/// Unsupported
442449
fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value>
443450
where
444451
V: Visitor<'de>,
@@ -460,20 +467,23 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
460467
}
461468
}
462469

470+
/// Unsupported. Use a more specific deserialize_* method
463471
fn deserialize_unit<V>(self, _visitor: V) -> Result<V::Value>
464472
where
465473
V: Visitor<'de>,
466474
{
467475
unreachable!()
468476
}
469477

478+
/// Unsupported. Use a more specific deserialize_* method
470479
fn deserialize_unit_struct<V>(self, _name: &'static str, _visitor: V) -> Result<V::Value>
471480
where
472481
V: Visitor<'de>,
473482
{
474483
unreachable!()
475484
}
476485

486+
/// Unsupported. We can’t parse newtypes because we don’t know the underlying type.
477487
fn deserialize_newtype_struct<V>(self, _name: &'static str, _visitor: V) -> Result<V::Value>
478488
where
479489
V: Visitor<'de>,
@@ -509,14 +519,17 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
509519
self,
510520
_name: &'static str,
511521
_len: usize,
512-
_visitor: V,
522+
visitor: V,
513523
) -> Result<V::Value>
514524
where
515525
V: Visitor<'de>,
516526
{
517-
unreachable!()
527+
self.deserialize_seq(visitor)
518528
}
519529

530+
/// Unsupported. Can’t make an arbitrary-sized map in no-std. Use a struct with a
531+
/// known format, or implement a custom map deserializer / visitor:
532+
/// https://serde.rs/deserialize-map.html
520533
fn deserialize_map<V>(self, _visitor: V) -> Result<V::Value>
521534
where
522535
V: Visitor<'de>,
@@ -570,20 +583,45 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
570583
self.deserialize_str(visitor)
571584
}
572585

573-
fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value>
586+
/// Used to throw out fields from JSON objects that we don’t want to
587+
/// keep in our structs.
588+
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
574589
where
575590
V: Visitor<'de>,
576591
{
577-
unreachable!()
592+
match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? {
593+
b'"' => self.deserialize_str(visitor),
594+
b'[' => self.deserialize_seq(visitor),
595+
b'{' => self.deserialize_struct("ignored", &[], visitor),
596+
b',' | b'}' | b']' => Err(Error::ExpectedSomeValue),
597+
// If it’s something else then we chomp until we get to an end delimiter.
598+
// This does technically allow for illegal JSON since we’re just ignoring
599+
// characters rather than parsing them.
600+
_ => loop {
601+
match self.peek() {
602+
// The visitor is expected to be UnknownAny’s visitor, which
603+
// implements visit_unit to return its unit Ok result.
604+
Some(b',') | Some(b'}') | Some(b']') => break visitor.visit_unit(),
605+
Some(_) => self.eat_char(),
606+
None => break Err(Error::EofWhileParsingString),
607+
}
608+
},
609+
}
578610
}
579611
}
580612

581613
impl de::Error for Error {
614+
// We can’t alloc a String to save the msg in, so we have this less-than-useful
615+
// error as better than panicking with unreachable!. These errors can arise from
616+
// derive, such as "not enough elements in a tuple" and "missing required field".
617+
//
618+
// TODO: consider using a heapless::String to save the first n characters of this
619+
// message.
582620
fn custom<T>(_msg: T) -> Self
583621
where
584622
T: fmt::Display,
585623
{
586-
unreachable!()
624+
Error::CustomError
587625
}
588626
}
589627

@@ -623,6 +661,7 @@ impl fmt::Display for Error {
623661
value."
624662
}
625663
Error::TrailingComma => "JSON has a comma after the last value in an array or map.",
664+
Error::CustomError => "JSON does not match deserializer’s expected format.",
626665
_ => "Invalid JSON",
627666
}
628667
)
@@ -830,6 +869,69 @@ mod tests {
830869
assert!(crate::from_str::<Temperature>(r#"{ "temperature": -1 }"#).is_err());
831870
}
832871

872+
#[test]
873+
fn struct_tuple() {
874+
#[derive(Debug, Deserialize, PartialEq)]
875+
struct Xy(i8, i8);
876+
877+
assert_eq!(crate::from_str(r#"[10, 20]"#), Ok(Xy(10, 20)));
878+
assert_eq!(crate::from_str(r#"[10, -20]"#), Ok(Xy(10, -20)));
879+
880+
// wrong number of args
881+
assert_eq!(crate::from_str::<Xy>(r#"[10]"#), Err(crate::de::Error::CustomError));
882+
assert_eq!(crate::from_str::<Xy>(r#"[10, 20, 30]"#), Err(crate::de::Error::TrailingCharacters));
883+
}
884+
885+
#[test]
886+
fn ignoring_extra_fields() {
887+
#[derive(Debug, Deserialize, PartialEq)]
888+
struct Temperature {
889+
temperature: u8,
890+
}
891+
892+
assert_eq!(
893+
crate::from_str(r#"{ "temperature": 20, "high": 80, "low": -10, "updated": true }"#),
894+
Ok(Temperature { temperature: 20 })
895+
);
896+
897+
assert_eq!(
898+
crate::from_str(
899+
r#"{ "temperature": 20, "conditions": "windy", "forecast": "cloudy" }"#
900+
),
901+
Ok(Temperature { temperature: 20 })
902+
);
903+
904+
assert_eq!(
905+
crate::from_str(r#"{ "temperature": 20, "hourly_conditions": ["windy", "rainy"] }"#),
906+
Ok(Temperature { temperature: 20 })
907+
);
908+
909+
assert_eq!(
910+
crate::from_str(r#"{ "temperature": 20, "source": { "station": "dock", "sensors": ["front", "back"] } }"#),
911+
Ok(Temperature { temperature: 20 })
912+
);
913+
914+
assert_eq!(
915+
crate::from_str(r#"{ "temperature": 20, "invalid": this-is-ignored }"#),
916+
Ok(Temperature { temperature: 20 })
917+
);
918+
919+
assert_eq!(
920+
crate::from_str::<Temperature>(r#"{ "temperature": 20, "broken": }"#),
921+
Err(crate::de::Error::ExpectedSomeValue)
922+
);
923+
924+
assert_eq!(
925+
crate::from_str::<Temperature>(r#"{ "temperature": 20, "broken": [ }"#),
926+
Err(crate::de::Error::ExpectedSomeValue)
927+
);
928+
929+
assert_eq!(
930+
crate::from_str::<Temperature>(r#"{ "temperature": 20, "broken": ] }"#),
931+
Err(crate::de::Error::ExpectedSomeValue)
932+
);
933+
}
934+
833935
// See https://iot.mozilla.org/wot/#thing-resource
834936
#[test]
835937
#[ignore]

0 commit comments

Comments
 (0)