winget-types 0.4.3

WinGet Types
Documentation
mod restricted;

use core::{fmt, str::FromStr};

use heapless::String;
pub use restricted::RestrictedCapability;
use thiserror::Error;

#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Capability {
    Activity,
    AllJoyn,
    Appointments,
    BackgroundMediaPlayback,
    BlockedChatMessages,
    Bluetooth,
    Chat,
    CodeGeneration,
    Contacts,
    GazeInput,
    GlobalMediaControl,
    GraphicsCapture,
    GraphicsCaptureProgrammatic,
    GraphicsCaptureWithoutBorder,
    HumanInterfaceDevice,
    HumanPresence,
    InternetClient,
    InternetClientServer,
    Location,
    LowLevel,
    LowLevelDevices,
    Microphone,
    MusicLibrary,
    Objects3D,
    Optical,
    PhoneCall,
    PhoneCallHistoryPublic,
    PicturesLibrary,
    PointOfService,
    PrivateNetworkClientServer,
    Proximity,
    Radios,
    RecordedCallsFolder,
    RemoteSystem,
    RemovableStorage,
    SerialCommunication,
    SpatialPerception,
    SystemManagement,
    Usb,
    UserAccountInformation,
    UserDataTasks,
    UserNotificationListener,
    VideosLibrary,
    VoipCall,
    Webcam,
    WiFiControl,
}

impl Capability {
    pub const MAX_LEN: usize = 40;

    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::Activity => "activity",
            Self::AllJoyn => "allJoyn",
            Self::Appointments => "appointments",
            Self::BackgroundMediaPlayback => "backgroundMediaPlayback",
            Self::BlockedChatMessages => "blockedChatMessages",
            Self::Bluetooth => "bluetooth",
            Self::Chat => "chat",
            Self::CodeGeneration => "codeGeneration",
            Self::Contacts => "contacts",
            Self::GazeInput => "gazeInput",
            Self::GlobalMediaControl => "globalMediaControl",
            Self::GraphicsCapture => "graphicsCapture",
            Self::GraphicsCaptureProgrammatic => "graphicsCaptureProgrammatic",
            Self::GraphicsCaptureWithoutBorder => "graphicsCaptureWithoutBorder",
            Self::HumanInterfaceDevice => "humaninterfacedevice",
            Self::HumanPresence => "humanPresence",
            Self::InternetClient => "internetClient",
            Self::InternetClientServer => "internetClientServer",
            Self::Location => "location",
            Self::LowLevel => "lowLevel",
            Self::LowLevelDevices => "lowLevelDevices",
            Self::Microphone => "microphone",
            Self::MusicLibrary => "musicLibrary",
            Self::Objects3D => "objects3D",
            Self::Optical => "optical",
            Self::PhoneCall => "phoneCall",
            Self::PhoneCallHistoryPublic => "phoneCallHistoryPublic",
            Self::PicturesLibrary => "picturesLibrary",
            Self::PointOfService => "pointOfService",
            Self::PrivateNetworkClientServer => "privateNetworkClientServer",
            Self::Proximity => "proximity",
            Self::Radios => "radios",
            Self::RecordedCallsFolder => "recordedCallsFolder",
            Self::RemoteSystem => "remoteSystem",
            Self::RemovableStorage => "removableStorage",
            Self::SerialCommunication => "serialcommunication",
            Self::SpatialPerception => "spatialPerception",
            Self::SystemManagement => "systemManagement",
            Self::Usb => "usb",
            Self::UserAccountInformation => "userAccountInformation",
            Self::UserDataTasks => "userDataTasks",
            Self::UserNotificationListener => "userNotificationListener",
            Self::VideosLibrary => "videosLibrary",
            Self::VoipCall => "voipCall",
            Self::Webcam => "webcam",
            Self::WiFiControl => "wiFiControl",
        }
    }
}

impl AsRef<str> for Capability {
    #[inline]
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl fmt::Display for Capability {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.as_str().fmt(f)
    }
}

#[derive(Debug, Error, Eq, PartialEq)]
pub enum CapabilityError {
    #[error("Capability must not be empty")]
    Empty,
    #[error(
        "Capability must not have more than {} ASCII characters but has {_0}",
        Capability::MAX_LEN
    )]
    TooLong(usize),
    #[error(r#""{_0}" is not a known capability"#)]
    Unknown(String<40>),
}

impl FromStr for Capability {
    type Err = CapabilityError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.is_empty() {
            return Err(Self::Err::Empty);
        }

        match s {
            "activity" => Ok(Self::Activity),
            "allJoyn" => Ok(Self::AllJoyn),
            "appointments" => Ok(Self::Appointments),
            "backgroundMediaPlayback" => Ok(Self::BackgroundMediaPlayback),
            "blockedChatMessages" => Ok(Self::BlockedChatMessages),
            "bluetooth" => Ok(Self::Bluetooth),
            "chat" => Ok(Self::Chat),
            "codeGeneration" => Ok(Self::CodeGeneration),
            "contacts" => Ok(Self::Contacts),
            "gazeInput" => Ok(Self::GazeInput),
            "globalMediaControl" => Ok(Self::GlobalMediaControl),
            "graphicsCapture" => Ok(Self::GraphicsCapture),
            "graphicsCaptureProgrammatic" => Ok(Self::GraphicsCaptureProgrammatic),
            "graphicsCaptureWithoutBorder" => Ok(Self::GraphicsCaptureWithoutBorder),
            "humaninterfacedevice" => Ok(Self::HumanInterfaceDevice),
            "humanPresence" => Ok(Self::HumanPresence),
            "internetClient" => Ok(Self::InternetClient),
            "internetClientServer" => Ok(Self::InternetClientServer),
            "location" => Ok(Self::Location),
            "lowLevel" => Ok(Self::LowLevel),
            "lowLevelDevices" => Ok(Self::LowLevelDevices),
            "microphone" => Ok(Self::Microphone),
            "musicLibrary" => Ok(Self::MusicLibrary),
            "objects3D" => Ok(Self::Objects3D),
            "optical" => Ok(Self::Optical),
            "phoneCall" => Ok(Self::PhoneCall),
            "phoneCallHistoryPublic" => Ok(Self::PhoneCallHistoryPublic),
            "picturesLibrary" => Ok(Self::PicturesLibrary),
            "pointOfService" => Ok(Self::PointOfService),
            "privateNetworkClientServer" => Ok(Self::PrivateNetworkClientServer),
            "proximity" => Ok(Self::Proximity),
            "radios" => Ok(Self::Radios),
            "recordedCallsFolder" => Ok(Self::RecordedCallsFolder),
            "remoteSystem" => Ok(Self::RemoteSystem),
            "removableStorage" => Ok(Self::RemovableStorage),
            "serialcommunication" => Ok(Self::SerialCommunication),
            "spatialPerception" => Ok(Self::SpatialPerception),
            "systemManagement" => Ok(Self::SystemManagement),
            "usb" => Ok(Self::Usb),
            "userAccountInformation" => Ok(Self::UserAccountInformation),
            "userDataTasks" => Ok(Self::UserDataTasks),
            "userNotificationListener" => Ok(Self::UserNotificationListener),
            "videosLibrary" => Ok(Self::VideosLibrary),
            "voipCall" => Ok(Self::VoipCall),
            "webcam" => Ok(Self::Webcam),
            "wiFiControl" => Ok(Self::WiFiControl),
            _ => Err(Self::Err::Unknown(
                s.parse::<String<{ Self::MAX_LEN }>>()
                    .map_err(|_| Self::Err::TooLong(s.len()))?,
            )),
        }
    }
}

#[cfg(feature = "serde")]
impl serde::Serialize for Capability {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.as_str().serialize(serializer)
    }
}

#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Capability {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct CapabilityVisitor;

        impl serde::de::Visitor<'_> for CapabilityVisitor {
            type Value = Capability;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("a capability string")
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                value.parse::<Self::Value>().map_err(E::custom)
            }

            fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                let utf8 = core::str::from_utf8(value).map_err(E::custom)?;
                self.visit_str(utf8)
            }
        }

        deserializer.deserialize_str(CapabilityVisitor)
    }
}

#[cfg(test)]
mod tests {
    use rstest::rstest;

    use super::{Capability, CapabilityError};

    #[rstest]
    #[case("musicLibrary")]
    #[case("picturesLibrary")]
    #[case("videosLibrary")]
    #[case("removableStorage")]
    #[case("internetClient")]
    #[case("internetClientServer")]
    #[case("privateNetworkClientServer")]
    #[case("contacts")]
    #[case("codeGeneration")]
    #[case("allJoyn")]
    #[case("phoneCall")]
    #[case("phoneCallHistoryPublic")]
    #[case("recordedCallsFolder")]
    #[case("userAccountInformation")]
    #[case("objects3D")]
    #[case("chat")]
    #[case("blockedChatMessages")]
    #[case("lowLevelDevices")]
    #[case("systemManagement")]
    #[case("backgroundMediaPlayback")]
    #[case("remoteSystem")]
    #[case("spatialPerception")]
    #[case("globalMediaControl")]
    #[case("graphicsCapture")]
    #[case("graphicsCaptureWithoutBorder")]
    #[case("graphicsCaptureProgrammatic")]
    #[case("userDataTasks")]
    #[case("userNotificationListener")]
    fn valid_general_capability(#[case] capability: &str) {
        assert!(capability.parse::<Capability>().is_ok());
    }

    #[rstest]
    #[case("location")]
    #[case("microphone")]
    #[case("proximity")]
    #[case("webcam")]
    #[case("usb")]
    #[case("humaninterfacedevice")]
    #[case("pointOfService")]
    #[case("bluetooth")]
    #[case("wiFiControl")]
    #[case("radios")]
    #[case("optical")]
    #[case("activity")]
    #[case("humanPresence")]
    #[case("serialcommunication")]
    #[case("gazeInput")]
    #[case("lowLevel")]
    fn valid_device_capability(#[case] capability: &str) {
        assert!(capability.parse::<Capability>().is_ok());
    }

    #[test]
    fn invalid_capability() {
        assert_eq!("".parse::<Capability>().err(), Some(CapabilityError::Empty));
    }
}