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)].