use core::{fmt, str::FromStr};
use bitflags::bitflags;
use thiserror::Error;
bitflags! {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Platform: u8 {
const WINDOWS_DESKTOP = 1;
const WINDOWS_UNIVERSAL = 1 << 1;
}
}
const WINDOWS_DESKTOP: &str = "Windows.Desktop";
const WINDOWS_UNIVERSAL: &str = "Windows.Universal";
impl fmt::Display for Platform {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::WINDOWS_DESKTOP => f.write_str(WINDOWS_DESKTOP),
Self::WINDOWS_UNIVERSAL => f.write_str(WINDOWS_UNIVERSAL),
_ => bitflags::parser::to_writer(self, f),
}
}
}
#[derive(Error, Debug, Eq, PartialEq)]
#[error("Platform did not match either `{WINDOWS_DESKTOP}` or `{WINDOWS_UNIVERSAL}`")]
pub struct PlatformParseError;
impl FromStr for Platform {
type Err = PlatformParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
WINDOWS_DESKTOP => Ok(Self::WINDOWS_DESKTOP),
WINDOWS_UNIVERSAL => Ok(Self::WINDOWS_UNIVERSAL),
_ => Err(PlatformParseError),
}
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Platform {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(Some(self.iter().count()))?;
for platform in self.iter() {
match platform {
Self::WINDOWS_DESKTOP => seq.serialize_element(WINDOWS_DESKTOP)?,
Self::WINDOWS_UNIVERSAL => seq.serialize_element(WINDOWS_UNIVERSAL)?,
_ => {}
}
}
seq.end()
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Platform {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct PlatformVisitor;
impl<'de> serde::de::Visitor<'de> for PlatformVisitor {
type Value = Platform;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a sequence of platform strings")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where
V: serde::de::SeqAccess<'de>,
{
let mut platform = Platform::empty();
while let Some(value) = seq.next_element::<&str>()? {
match value {
WINDOWS_DESKTOP => platform |= Platform::WINDOWS_DESKTOP,
WINDOWS_UNIVERSAL => platform |= Platform::WINDOWS_UNIVERSAL,
_ => {
return Err(serde::de::Error::unknown_variant(
value,
&[WINDOWS_DESKTOP, WINDOWS_UNIVERSAL],
));
}
}
}
Ok(platform)
}
}
deserializer.deserialize_seq(PlatformVisitor)
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "serde")]
use indoc::indoc;
#[cfg(feature = "serde")]
use rstest::rstest;
use super::{Platform, PlatformParseError};
#[cfg(feature = "serde")]
#[rstest]
#[case(
Platform::all(),
indoc! {"
- Windows.Desktop
- Windows.Universal
"}
)]
#[case(
Platform::empty(),
indoc! {"
[]
"}
)]
#[case(
Platform::WINDOWS_DESKTOP,
indoc! {"
- Windows.Desktop
"}
)]
#[case(
Platform::WINDOWS_UNIVERSAL,
indoc! {"
- Windows.Universal
"}
)]
fn serialize_platform(#[case] platform: Platform, #[case] expected: &str) {
assert_eq!(serde_yaml::to_string(&platform).unwrap(), expected);
}
#[cfg(feature = "serde")]
#[rstest]
#[case(
indoc! {"
- Windows.Desktop
- Windows.Universal
"},
Platform::all(),
)]
#[case(
indoc! {"
[]
"},
Platform::empty()
)]
#[case(
indoc! {"
- Windows.Desktop
"},
Platform::WINDOWS_DESKTOP
)]
#[case(
indoc! {"
- Windows.Universal
"},
Platform::WINDOWS_UNIVERSAL
)]
fn deserialize_platform(#[case] input: &str, #[case] expected: Platform) {
assert_eq!(serde_yaml::from_str::<Platform>(input).unwrap(), expected);
}
#[cfg(feature = "serde")]
#[test]
fn platform_serialize_ordered() {
let input = indoc! {"
- Windows.Universal
- Windows.Desktop
"};
let deserialized = serde_yaml::from_str::<Platform>(input).unwrap();
assert_eq!(
serde_yaml::to_string(&deserialized).unwrap(),
indoc! {"
- Windows.Desktop
- Windows.Universal
"}
);
}
#[test]
fn from_str() {
assert!("Windows.Desktop".parse::<Platform>().is_ok());
assert!("Windows.Universal".parse::<Platform>().is_ok());
assert_eq!(
"WindowsDesktop".parse::<Platform>().err().unwrap(),
PlatformParseError
);
}
}