Add support for deserializing Vec<Value<String>> in config.

This adds the ability to track the definition location of a string
in a TOML array.
This commit is contained in:
Eric Huss 2022-12-09 20:03:27 -08:00 committed by Pietro Albini
parent c9bff1ec6d
commit 95c98116e3
No known key found for this signature in database
GPG Key ID: CD76B35F7734769E
1 changed files with 101 additions and 6 deletions

View File

@ -384,7 +384,12 @@ impl<'de> de::SeqAccess<'de> for ConfigSeqAccess {
{
match self.list_iter.next() {
// TODO: add `def` to error?
Some((value, _def)) => seed.deserialize(value.into_deserializer()).map(Some),
Some((value, def)) => {
// This might be a String or a Value<String>.
// ValueDeserializer will handle figuring out which one it is.
let maybe_value_de = ValueDeserializer::new_with_string(value, def);
seed.deserialize(maybe_value_de).map(Some)
}
None => Ok(None),
}
}
@ -400,7 +405,17 @@ impl<'de> de::SeqAccess<'de> for ConfigSeqAccess {
struct ValueDeserializer<'config> {
hits: u32,
definition: Definition,
de: Deserializer<'config>,
/// The deserializer, used to actually deserialize a Value struct.
/// This is `None` if deserializing a string.
de: Option<Deserializer<'config>>,
/// A string value to deserialize.
///
/// This is used for situations where you can't address a string via a
/// TOML key, such as a string inside an array. The `ConfigSeqAccess`
/// doesn't know if the type it should deserialize to is a `String` or
/// `Value<String>`, so `ValueDeserializer` needs to be able to handle
/// both.
str_value: Option<String>,
}
impl<'config> ValueDeserializer<'config> {
@ -428,9 +443,19 @@ impl<'config> ValueDeserializer<'config> {
Ok(ValueDeserializer {
hits: 0,
definition,
de,
de: Some(de),
str_value: None,
})
}
fn new_with_string(s: String, definition: Definition) -> ValueDeserializer<'config> {
ValueDeserializer {
hits: 0,
definition,
de: None,
str_value: Some(s),
}
}
}
impl<'de, 'config> de::MapAccess<'de> for ValueDeserializer<'config> {
@ -459,9 +484,14 @@ impl<'de, 'config> de::MapAccess<'de> for ValueDeserializer<'config> {
// If this is the first time around we deserialize the `value` field
// which is the actual deserializer
if self.hits == 1 {
return seed
.deserialize(self.de.clone())
.map_err(|e| e.with_key_context(&self.de.key, self.definition.clone()));
if let Some(de) = &self.de {
return seed
.deserialize(de.clone())
.map_err(|e| e.with_key_context(&de.key, self.definition.clone()));
} else {
return seed
.deserialize(self.str_value.as_ref().unwrap().clone().into_deserializer());
}
}
// ... otherwise we're deserializing the `definition` field, so we need
@ -484,6 +514,71 @@ impl<'de, 'config> de::MapAccess<'de> for ValueDeserializer<'config> {
}
}
// Deserializer is only implemented to handle deserializing a String inside a
// sequence (like `Vec<String>` or `Vec<Value<String>>`). `Value<String>` is
// handled by deserialize_struct, and the plain `String` is handled by all the
// other functions here.
impl<'de, 'config> de::Deserializer<'de> for ValueDeserializer<'config> {
type Error = ConfigError;
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_str(&self.str_value.expect("string expected"))
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_string(self.str_value.expect("string expected"))
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
// Match on the magical struct name/field names that are passed in to
// detect when we're deserializing `Value<T>`.
//
// See more comments in `value.rs` for the protocol used here.
if name == value::NAME && fields == value::FIELDS {
return visitor.visit_map(self);
}
unimplemented!("only strings and Value can be deserialized from a sequence");
}
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_string(self.str_value.expect("string expected"))
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_unit()
}
serde::forward_to_deserialize_any! {
i8 i16 i32 i64
u8 u16 u32 u64
option
newtype_struct seq tuple tuple_struct map enum bool
f32 f64 char bytes
byte_buf unit unit_struct
identifier
}
}
/// A deserializer which takes two values and deserializes into a tuple of those
/// two values. This is similar to types like `StrDeserializer` in upstream
/// serde itself.