I'm trying to deserialize a map where all possible values are structs implementing a certain CompletionSource
trait without going over each item individually.
More specifically (leaving out the irrelevant parts):
trait CompletionSource { /* .. */ }
#[derive(Deserialize)]
struct SourceA {
enable: bool,
}
impl CompletionSource for SourceA { /* .. */ }
#[derive(Deserialize)]
struct SourceB {
enable: bool,
}
impl CompletionSource for SourceB { /* .. */ }
#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
enum ValidSource {
SourceA,
SourceB,
}
The data to deserialize looks like
source_a = {
enable = true,
},
source_b = {
enable = false,
},
and I'd like to get a Vec<Arc<dyn CompletionSource>>
out of that. To do that I've implemented serde::de::Visitor
for a SourcesVisitor
struct:
struct SourcesVisitor;
impl<'de> Visitor<'de> for SourcesVisitor {
type Value = Vec<Arc<dyn CompletionSource>>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a list of completion sources")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut sources = match access.size_hint() {
Some(len) => Vec::with_capacity(len),
None => Vec::new(),
};
while let Some(source) = access.next_key::<ValidSource>()? {
match source {
ValidSource::SourceA => {
let a = access.next_value::<SourceA>()?;
if a.enable {
sources.push(Arc::new(a) as Arc<dyn CompletionSource>);
}
},
ValidSource::SourceB => {
let b = access.next_value::<SourceB>()?;
if b.enable {
sources.push(Arc::new(b) as Arc<dyn CompletionSource>);
}
},
}
}
Ok(sources)
}
}
which works, but every time I add a new source I need add a new variant to the ValidSource
enum and its corresponding arm in match source { .. }
. Since all the arms of the match look exactly the same this feels a bit boilerplatey.
Is there a way to have an implementation of visit_map
that doesn't change as the number of possible sources increases? Maybe by turning the variants of the ValidSource
enum from units to tuples? I.e. from
enum ValidSource {
SourceA,
SourceB,
}
to (apologies for naming the enum variants the same as the struct they contain)
enum ValidSource {
SourceA(SourceA),
SourceB(SourceB),
}