blob: e1a460b91eca097ee43271e55bda05fc75874bf0 [file] [log] [blame]
use core::{fmt, str};
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
pub fn serialize_bits_default<T: fmt::Display + AsRef<B>, B: Serialize, S: Serializer>(
flags: &T,
serializer: S,
) -> Result<S::Ok, S::Error> {
// Serialize human-readable flags as a string like `"A | B"`
if serializer.is_human_readable() {
serializer.collect_str(flags)
}
// Serialize non-human-readable flags directly as the underlying bits
else {
flags.as_ref().serialize(serializer)
}
}
pub fn deserialize_bits_default<
'de,
T: str::FromStr + From<B>,
B: Deserialize<'de>,
D: Deserializer<'de>,
>(
deserializer: D,
) -> Result<T, D::Error>
where
<T as str::FromStr>::Err: fmt::Display,
{
if deserializer.is_human_readable() {
// Deserialize human-readable flags by parsing them from strings like `"A | B"`
struct FlagsVisitor<T>(core::marker::PhantomData<T>);
impl<'de, T: str::FromStr> Visitor<'de> for FlagsVisitor<T>
where
<T as str::FromStr>::Err: fmt::Display,
{
type Value = T;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string value of `|` separated flags")
}
fn visit_str<E: Error>(self, flags: &str) -> Result<Self::Value, E> {
flags.parse().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::deserialize(deserializer)?;
Ok(bits.into())
}
}
#[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)]);
}
}