//! Specialized serialization for flags types using `serde`. use crate::{ parser::{self, ParseHex, WriteHex}, Flags, }; use core::{fmt, str}; use serde::{ de::{Error, Visitor}, Deserialize, Deserializer, Serialize, Serializer, }; /** Serialize a set of flags as a human-readable string or their underlying bits. Any unknown bits will be retained. */ pub fn serialize(flags: &B, serializer: S) -> Result where B::Bits: WriteHex + Serialize, { // Serialize human-readable flags as a string like `"A | B"` if serializer.is_human_readable() { serializer.collect_str(&parser::AsDisplay(flags)) } // Serialize non-human-readable flags directly as the underlying bits else { flags.bits().serialize(serializer) } } /** Deserialize a set of flags from a human-readable string or their underlying bits. Any unknown bits will be retained. */ pub fn deserialize<'de, B: Flags, D: Deserializer<'de>>(deserializer: D) -> Result where B::Bits: ParseHex + Deserialize<'de>, { if deserializer.is_human_readable() { // Deserialize human-readable flags by parsing them from strings like `"A | B"` struct FlagsVisitor(core::marker::PhantomData); impl<'de, B: Flags> Visitor<'de> for FlagsVisitor where B::Bits: ParseHex, { type Value = B; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a string value of `|` separated flags") } fn visit_str(self, flags: &str) -> Result { parser::from_str(flags).map_err(|e| E::custom(e)) } } deserializer.deserialize_str(FlagsVisitor(Default::default())) } else { // Deserialize non-human-readable flags directly from the underlying bits let bits = B::Bits::deserialize(deserializer)?; Ok(B::from_bits_retain(bits)) } } #[cfg(test)] mod tests { use serde_test::{assert_tokens, Configure, Token::*}; bitflags! { #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug, PartialEq, Eq)] #[serde(transparent)] struct SerdeFlags: u32 { const A = 1; const B = 2; const C = 4; const D = 8; } } #[test] fn test_serde_bitflags_default() { assert_tokens(&SerdeFlags::empty().readable(), &[Str("")]); assert_tokens(&SerdeFlags::empty().compact(), &[U32(0)]); assert_tokens(&(SerdeFlags::A | SerdeFlags::B).readable(), &[Str("A | B")]); assert_tokens(&(SerdeFlags::A | SerdeFlags::B).compact(), &[U32(1 | 2)]); } }