Not quite. I'm saying that I'm trying to deserialize, from the deserializer, into a WidgetConfig
using a GlobalWidgetConfig
as a seed value. It's essentially the same as a regular Deserialize
impl, except it has a state that it can use as a resource. So when I'm deserializing from the deserializer into WidgetConfig
, I do everything like you normally would, except if a value isn't provided I can look at the GlobalWidgetConfig
seed value and copy data from there.
When you call next_value()
, you're asking it to use the Deserialize
impl of a type to deserialize into the value. When you call next_value_seed(seed)
, you're asking it to use the DeserializeSeed
impl of a type (call it T
) to deserialize into the value specified by <T as DeserializeSeed>::Value
.
Under the hood, calls to next_value()
actually end up forwarding to next_value_seed()
, but that's just an implementation detail. Essentially, it's doing basically the same thing except that it uses a seed value to pass state into deserialization.
You're on the right track. The main issue here is that you're assigning widgets
to be an object of type Option<WidgetConfig>
with the line:
widgets = Some(map.next_value_seed(&global_config)?);
but your struct
creation here expects that type to be Option<Vec<WidgetConfig>>
:
Ok(Config {
global_config,
widgets: widgets.ok_or_else(|| de::Error::missing_field("widgets"))?,
})
hence the error message. What you need to do is deserialize the value accompanying the Field::Widgets
key as a Vec<WidgetConfig>
, rather than just a single WidgetConfig
.
I should note that the toml
input
[[widgets]]
fg_color = \"blue\"
bg_color = \"red\"
size = 24
[[widgets]]
size = 24
will actually be deserialized as a key-value pair of a single identifier ("widgets") and a serde
seq type, not as a bunch of key-value pairs of "widget" identifiers with serde
struct types.
So you need to modify your DeserializeSeed
implementation for GlobalWidgetConfig
to tell the deserializer it expects to deserialize a seq
type, and also change the Value
associated type to Vec<WidgetConfig>
. Then you just deserialize each value in the sequence as a WidgetConfig
and output the result.
I would rewrite the DeserializeSeed
impl like this:
impl<'de, 'a> DeserializeSeed<'de> for &'a GlobalWidgetConfig {
type Value = Vec<WidgetConfig>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
struct WidgetConfigSeqVisitor<'a>(&'a GlobalWidgetConfig);
impl<'de, 'a> Visitor<'de> for WidgetConfigSeqVisitor<'a> {
type Value = Vec<WidgetConfig>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("sequence of WidgetConfigs")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where
V: SeqAccess<'de>,
{
// You can make this a bit more efficient using seq.size_hint()
// and Vec::with_capacity().
let mut result = Vec::new();
while let Some(widget_config) =
seq.next_element_seed(WidgetConfigDeserializer(self.0))?
{
result.push(widget_config);
}
Ok(result)
}
}
deserializer.deserialize_seq(WidgetConfigSeqVisitor(self))
}
}
You'll notice I refer to a new type, WidgetConfigDeserializer
. You'll still need all of the old logic to actually deserialize a WidgetConfig
using the GlobalWidgetConfig
as a seed, but since I just wrote this new implementation on GlobalWidgetConfig
outputting a Vec<WidgetConfig>
, I need to define a separate type to have the DeserializeSeed<Value = WidgetConfig>
implementation we used earlier. That ended up looking like this for me (note that it's the exact same logic as before, just implemented on a different type that simply wraps a reference to the GlobalWidgetConfig
):
struct WidgetConfigDeserializer<'a>(&'a GlobalWidgetConfig);
// This deserializes using an already-existing `GlobalWidgetConfig` as a base.
impl<'de, 'a> DeserializeSeed<'de> for WidgetConfigDeserializer<'a> {
type Value = WidgetConfig;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "snake_case")]
enum Field {
FgColor,
BgColor,
Size,
// Other fields
}
struct WidgetConfigVisitor<'a>(&'a GlobalWidgetConfig);
impl<'de, 'a> Visitor<'de> for WidgetConfigVisitor<'a> {
type Value = WidgetConfig;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct WidgetConfig")
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: MapAccess<'de>,
{
let mut fg_color = None;
let mut bg_color = None;
let mut size = None;
// Other fields
while let Some(key) = map.next_key()? {
match key {
Field::FgColor => {
if fg_color.is_some() {
return Err(de::Error::duplicate_field("fg_color"));
}
fg_color = Some(map.next_value()?);
}
Field::BgColor => {
if bg_color.is_some() {
return Err(de::Error::duplicate_field("bg_color"));
}
bg_color = Some(map.next_value()?);
}
Field::Size => {
if size.is_some() {
return Err(de::Error::duplicate_field("size"));
}
size = Some(map.next_value()?);
} // Other fields
}
}
Ok(WidgetConfig {
fg_color: fg_color
.or_else(|| self.0.fg_color.clone())
.ok_or_else(|| de::Error::missing_field("fg_color"))?,
bg_color: bg_color
.or_else(|| self.0.bg_color.clone())
.ok_or_else(|| de::Error::missing_field("bg_color"))?,
size: size
.or_else(|| self.0.size.clone())
.ok_or_else(|| de::Error::missing_field("size"))?,
// Other fields
})
}
}
const FIELDS: &[&str] = &[
"fg_color", "bg_color", "size",
// Other fields
];
deserializer.deserialize_struct("WidgetConfig", FIELDS, WidgetConfigVisitor(self.0))
}
}
So now everything else in your example should work, because the widgets = Some(map.next_value_seed(&global_config)?);
line will now assign a value of type Option<Vec<WidgetConfig>>
to widgets
, which can then be populated into your Config
struct. I have a working version of your example here: Rust Playground using the code I mentioned above. It should work with any number of widgets, like you'd expect.
Hope that helps. I tried to provide my reasoning instead of just dumping code at you, because manual Deserialize
implementations tend to be very long (interestingly, manual Serialize
implementations are not nearly as long). It's no wonder there has been so much effort put in to writing procedural macros to generate this code for us, and it's too bad this more complicated use case can't just be derived by #[derive(Deserialize)]
.