Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Way to support alternative serde format + being able to access the number of variants #43

Open
aldanor opened this issue Jul 1, 2022 · 2 comments

Comments

@aldanor
Copy link

aldanor commented Jul 1, 2022

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.

@aldanor
Copy link
Author

aldanor commented Jul 3, 2022

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))
        }
    }

@meithecatte
Copy link
Owner

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants