enumflags2
enumflags2 copied to clipboard
Way to support alternative serde format + being able to access the number of variants
Here's a thought, something that happens often in practice: you're deserializing something that looks like
{"flags": ["foo", "baz"]}
into your own bitflags format which looks like
enum Flags { Foo, Bar, Baz }
There's a few ways to do this manually, but wouldn't it be nice if this crate provided some way to do it? Not sure what's the best way, since making it a crate feature would immediately change it for all wrapped enums, whereas it's more of a runtime thing. Perhaps the deserializer could try parsing a sequence if it can't parse an integer? There shouldn't be any ambiguity there. Or maybe a separate public module that can be used with #[serde(with)]
- this way some fields can be (de)serialized as ints and some - as sequences.
Also, if doing it by hand and in order to avoid allocations, you might need something like T::N_VARIANTS
or ArrayVec<T, T::N_VARIANTS>
(if you're sure the flags don't repeat...) so you can instantiate something like [T; T::N_VARIANTS]
which currently is not something exposed by any of the traits unless I'm missing something.
Here's something that sort of works with #[serde(with = ...)]
as an example (but would be nice to have it work without allocation when deserializing):
pub mod enumflags2_seq {
use serde::{de::DeserializeOwned, ser::SerializeSeq};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use enumflags2::{BitFlag, BitFlags};
pub fn serialize<S, T>(v: &BitFlags<T>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: BitFlag + Serialize,
{
let mut seq = s.serialize_seq(Some(v.len()))?;
for flag in v.iter() {
seq.serialize_element(&flag)?;
}
seq.end()
}
pub fn deserialize<'de, D, T>(d: D) -> Result<BitFlags<T>, D::Error>
where
D: Deserializer<'de>,
T: BitFlag + DeserializeOwned,
{
// TODO: possible to do without allocation?
let flags = Vec::<T>::deserialize(d)?;
Ok(BitFlags::from_iter(flags))
}
}
Vec::deserialize
is not magic. You should have no trouble adapting the implementation to insert directly into a BitFlags
instead of going through the Vec
.
Attempting to parse both an integer and a sequence could make sense, but there's no equivalent for serializing – you have to make a choice.
If you want to submit a PR with a module for use with #[serde(with = "...")]
, I'll happily merge that.