winget_types/installer/capability/
mod.rs

1mod restricted;
2
3use core::{fmt, str::FromStr};
4
5use heapless::String;
6pub use restricted::RestrictedCapability;
7use thiserror::Error;
8
9#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
10pub enum Capability {
11    Activity,
12    AllJoyn,
13    Appointments,
14    BackgroundMediaPlayback,
15    BlockedChatMessages,
16    Bluetooth,
17    Chat,
18    CodeGeneration,
19    Contacts,
20    GazeInput,
21    GlobalMediaControl,
22    GraphicsCapture,
23    GraphicsCaptureProgrammatic,
24    GraphicsCaptureWithoutBorder,
25    HumanInterfaceDevice,
26    HumanPresence,
27    InternetClient,
28    InternetClientServer,
29    Location,
30    LowLevel,
31    LowLevelDevices,
32    Microphone,
33    MusicLibrary,
34    Objects3D,
35    Optical,
36    PhoneCall,
37    PhoneCallHistoryPublic,
38    PicturesLibrary,
39    PointOfService,
40    PrivateNetworkClientServer,
41    Proximity,
42    Radios,
43    RecordedCallsFolder,
44    RemoteSystem,
45    RemovableStorage,
46    SerialCommunication,
47    SpatialPerception,
48    SystemManagement,
49    Usb,
50    UserAccountInformation,
51    UserDataTasks,
52    UserNotificationListener,
53    VideosLibrary,
54    VoipCall,
55    Webcam,
56    WiFiControl,
57}
58
59impl Capability {
60    pub const MAX_LEN: usize = 40;
61
62    #[must_use]
63    pub const fn as_str(self) -> &'static str {
64        match self {
65            Self::Activity => "activity",
66            Self::AllJoyn => "allJoyn",
67            Self::Appointments => "appointments",
68            Self::BackgroundMediaPlayback => "backgroundMediaPlayback",
69            Self::BlockedChatMessages => "blockedChatMessages",
70            Self::Bluetooth => "bluetooth",
71            Self::Chat => "chat",
72            Self::CodeGeneration => "codeGeneration",
73            Self::Contacts => "contacts",
74            Self::GazeInput => "gazeInput",
75            Self::GlobalMediaControl => "globalMediaControl",
76            Self::GraphicsCapture => "graphicsCapture",
77            Self::GraphicsCaptureProgrammatic => "graphicsCaptureProgrammatic",
78            Self::GraphicsCaptureWithoutBorder => "graphicsCaptureWithoutBorder",
79            Self::HumanInterfaceDevice => "humaninterfacedevice",
80            Self::HumanPresence => "humanPresence",
81            Self::InternetClient => "internetClient",
82            Self::InternetClientServer => "internetClientServer",
83            Self::Location => "location",
84            Self::LowLevel => "lowLevel",
85            Self::LowLevelDevices => "lowLevelDevices",
86            Self::Microphone => "microphone",
87            Self::MusicLibrary => "musicLibrary",
88            Self::Objects3D => "objects3D",
89            Self::Optical => "optical",
90            Self::PhoneCall => "phoneCall",
91            Self::PhoneCallHistoryPublic => "phoneCallHistoryPublic",
92            Self::PicturesLibrary => "picturesLibrary",
93            Self::PointOfService => "pointOfService",
94            Self::PrivateNetworkClientServer => "privateNetworkClientServer",
95            Self::Proximity => "proximity",
96            Self::Radios => "radios",
97            Self::RecordedCallsFolder => "recordedCallsFolder",
98            Self::RemoteSystem => "remoteSystem",
99            Self::RemovableStorage => "removableStorage",
100            Self::SerialCommunication => "serialcommunication",
101            Self::SpatialPerception => "spatialPerception",
102            Self::SystemManagement => "systemManagement",
103            Self::Usb => "usb",
104            Self::UserAccountInformation => "userAccountInformation",
105            Self::UserDataTasks => "userDataTasks",
106            Self::UserNotificationListener => "userNotificationListener",
107            Self::VideosLibrary => "videosLibrary",
108            Self::VoipCall => "voipCall",
109            Self::Webcam => "webcam",
110            Self::WiFiControl => "wiFiControl",
111        }
112    }
113}
114
115impl AsRef<str> for Capability {
116    #[inline]
117    fn as_ref(&self) -> &str {
118        self.as_str()
119    }
120}
121
122impl fmt::Display for Capability {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        self.as_str().fmt(f)
125    }
126}
127
128#[derive(Debug, Error, Eq, PartialEq)]
129pub enum CapabilityError {
130    #[error("Capability must not be empty")]
131    Empty,
132    #[error(
133        "Capability must not have more than {} ASCII characters but has {_0}",
134        Capability::MAX_LEN
135    )]
136    TooLong(usize),
137    #[error(r#""{_0}" is not a known capability"#)]
138    Unknown(String<40>),
139}
140
141impl FromStr for Capability {
142    type Err = CapabilityError;
143
144    fn from_str(s: &str) -> Result<Self, Self::Err> {
145        if s.is_empty() {
146            return Err(Self::Err::Empty);
147        }
148
149        match s {
150            "activity" => Ok(Self::Activity),
151            "allJoyn" => Ok(Self::AllJoyn),
152            "appointments" => Ok(Self::Appointments),
153            "backgroundMediaPlayback" => Ok(Self::BackgroundMediaPlayback),
154            "blockedChatMessages" => Ok(Self::BlockedChatMessages),
155            "bluetooth" => Ok(Self::Bluetooth),
156            "chat" => Ok(Self::Chat),
157            "codeGeneration" => Ok(Self::CodeGeneration),
158            "contacts" => Ok(Self::Contacts),
159            "gazeInput" => Ok(Self::GazeInput),
160            "globalMediaControl" => Ok(Self::GlobalMediaControl),
161            "graphicsCapture" => Ok(Self::GraphicsCapture),
162            "graphicsCaptureProgrammatic" => Ok(Self::GraphicsCaptureProgrammatic),
163            "graphicsCaptureWithoutBorder" => Ok(Self::GraphicsCaptureWithoutBorder),
164            "humaninterfacedevice" => Ok(Self::HumanInterfaceDevice),
165            "humanPresence" => Ok(Self::HumanPresence),
166            "internetClient" => Ok(Self::InternetClient),
167            "internetClientServer" => Ok(Self::InternetClientServer),
168            "location" => Ok(Self::Location),
169            "lowLevel" => Ok(Self::LowLevel),
170            "lowLevelDevices" => Ok(Self::LowLevelDevices),
171            "microphone" => Ok(Self::Microphone),
172            "musicLibrary" => Ok(Self::MusicLibrary),
173            "objects3D" => Ok(Self::Objects3D),
174            "optical" => Ok(Self::Optical),
175            "phoneCall" => Ok(Self::PhoneCall),
176            "phoneCallHistoryPublic" => Ok(Self::PhoneCallHistoryPublic),
177            "picturesLibrary" => Ok(Self::PicturesLibrary),
178            "pointOfService" => Ok(Self::PointOfService),
179            "privateNetworkClientServer" => Ok(Self::PrivateNetworkClientServer),
180            "proximity" => Ok(Self::Proximity),
181            "radios" => Ok(Self::Radios),
182            "recordedCallsFolder" => Ok(Self::RecordedCallsFolder),
183            "remoteSystem" => Ok(Self::RemoteSystem),
184            "removableStorage" => Ok(Self::RemovableStorage),
185            "serialcommunication" => Ok(Self::SerialCommunication),
186            "spatialPerception" => Ok(Self::SpatialPerception),
187            "systemManagement" => Ok(Self::SystemManagement),
188            "usb" => Ok(Self::Usb),
189            "userAccountInformation" => Ok(Self::UserAccountInformation),
190            "userDataTasks" => Ok(Self::UserDataTasks),
191            "userNotificationListener" => Ok(Self::UserNotificationListener),
192            "videosLibrary" => Ok(Self::VideosLibrary),
193            "voipCall" => Ok(Self::VoipCall),
194            "webcam" => Ok(Self::Webcam),
195            "wiFiControl" => Ok(Self::WiFiControl),
196            _ => Err(Self::Err::Unknown(
197                s.parse::<String<{ Self::MAX_LEN }>>()
198                    .map_err(|_| Self::Err::TooLong(s.len()))?,
199            )),
200        }
201    }
202}
203
204#[cfg(feature = "serde")]
205impl serde::Serialize for Capability {
206    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
207    where
208        S: serde::Serializer,
209    {
210        self.as_str().serialize(serializer)
211    }
212}
213
214#[cfg(feature = "serde")]
215impl<'de> serde::Deserialize<'de> for Capability {
216    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
217    where
218        D: serde::Deserializer<'de>,
219    {
220        struct CapabilityVisitor;
221
222        impl serde::de::Visitor<'_> for CapabilityVisitor {
223            type Value = Capability;
224
225            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
226                formatter.write_str("a capability string")
227            }
228
229            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
230            where
231                E: serde::de::Error,
232            {
233                value.parse::<Self::Value>().map_err(E::custom)
234            }
235
236            fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
237            where
238                E: serde::de::Error,
239            {
240                let utf8 = core::str::from_utf8(value).map_err(E::custom)?;
241                self.visit_str(utf8)
242            }
243        }
244
245        deserializer.deserialize_str(CapabilityVisitor)
246    }
247}
248
249#[cfg(test)]
250mod tests {
251    use rstest::rstest;
252
253    use super::{Capability, CapabilityError};
254
255    #[rstest]
256    #[case("musicLibrary")]
257    #[case("picturesLibrary")]
258    #[case("videosLibrary")]
259    #[case("removableStorage")]
260    #[case("internetClient")]
261    #[case("internetClientServer")]
262    #[case("privateNetworkClientServer")]
263    #[case("contacts")]
264    #[case("codeGeneration")]
265    #[case("allJoyn")]
266    #[case("phoneCall")]
267    #[case("phoneCallHistoryPublic")]
268    #[case("recordedCallsFolder")]
269    #[case("userAccountInformation")]
270    #[case("objects3D")]
271    #[case("chat")]
272    #[case("blockedChatMessages")]
273    #[case("lowLevelDevices")]
274    #[case("systemManagement")]
275    #[case("backgroundMediaPlayback")]
276    #[case("remoteSystem")]
277    #[case("spatialPerception")]
278    #[case("globalMediaControl")]
279    #[case("graphicsCapture")]
280    #[case("graphicsCaptureWithoutBorder")]
281    #[case("graphicsCaptureProgrammatic")]
282    #[case("userDataTasks")]
283    #[case("userNotificationListener")]
284    fn valid_general_capability(#[case] capability: &str) {
285        assert!(capability.parse::<Capability>().is_ok());
286    }
287
288    #[rstest]
289    #[case("location")]
290    #[case("microphone")]
291    #[case("proximity")]
292    #[case("webcam")]
293    #[case("usb")]
294    #[case("humaninterfacedevice")]
295    #[case("pointOfService")]
296    #[case("bluetooth")]
297    #[case("wiFiControl")]
298    #[case("radios")]
299    #[case("optical")]
300    #[case("activity")]
301    #[case("humanPresence")]
302    #[case("serialcommunication")]
303    #[case("gazeInput")]
304    #[case("lowLevel")]
305    fn valid_device_capability(#[case] capability: &str) {
306        assert!(capability.parse::<Capability>().is_ok());
307    }
308
309    #[test]
310    fn invalid_capability() {
311        assert_eq!("".parse::<Capability>().err(), Some(CapabilityError::Empty));
312    }
313}