Skip to main content

wslplugins_rs/wsl_version/
capability.rs

1use super::WSLVersion;
2use crate::api::errors::RequirementDefinition;
3use std::collections::HashSet;
4use strum::{Display, EnumIter};
5
6/// WSL Plugin API capabilities that are only available from a specific API version.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumIter)]
8#[non_exhaustive]
9pub enum WSLVersionCapability {
10    /// Access to `WSLDistributionInformation::InitPid` (`2.0.5`).
11    DistributionInitPid,
12    /// Notification sent when a distribution is registered (`2.1.2`).
13    DistributionRegisteredHook,
14    /// Notification sent when a distribution is unregistered (`2.1.2`).
15    DistributionUnregisteredHook,
16    /// Execute a command inside a specific user distribution (`2.1.2`).
17    ExecuteBinaryInDistribution,
18    /// Access to the distribution flavor field (`2.4.4`).
19    DistributionFlavor,
20    /// Access to the distribution version field (`2.4.4`).
21    DistributionVersion,
22}
23
24impl WSLVersionCapability {
25    /// Returns the minimum WSL Plugin API version required for this capability.
26    #[must_use]
27    #[inline]
28    pub const fn required_version(self) -> WSLVersion {
29        match self {
30            Self::DistributionInitPid => WSLVersion::new(2, 0, 5),
31            Self::DistributionRegisteredHook
32            | Self::DistributionUnregisteredHook
33            | Self::ExecuteBinaryInDistribution => WSLVersion::new(2, 1, 2),
34            Self::DistributionFlavor | Self::DistributionVersion => WSLVersion::new(2, 4, 4),
35        }
36    }
37}
38
39impl TryFrom<RequirementDefinition> for WSLVersionCapability {
40    type Error = RequirementDefinition;
41
42    #[inline]
43    fn try_from(value: RequirementDefinition) -> std::result::Result<Self, Self::Error> {
44        match value {
45            RequirementDefinition::Capabilities(capabilities) if capabilities.len() == 1 => {
46                capabilities
47                    .into_iter()
48                    .next()
49                    .ok_or_else(|| RequirementDefinition::Capabilities(HashSet::new()))
50            }
51            requirement => Err(requirement),
52        }
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    use proptest::prelude::*;
60    use strum::IntoEnumIterator as _;
61
62    fn arb_capability() -> impl Strategy<Value = WSLVersionCapability> {
63        prop::sample::select(WSLVersionCapability::iter().collect::<Vec<_>>())
64    }
65
66    #[test]
67    fn single_capability_requirement_can_be_recovered() {
68        let capability = WSLVersionCapability::ExecuteBinaryInDistribution;
69        let requirement = RequirementDefinition::from(capability);
70
71        assert_eq!(WSLVersionCapability::try_from(requirement), Ok(capability));
72    }
73
74    #[test]
75    fn multi_capability_requirement_cannot_be_recovered_as_single_capability() {
76        let requirement = RequirementDefinition::from([
77            WSLVersionCapability::DistributionRegisteredHook,
78            WSLVersionCapability::DistributionUnregisteredHook,
79        ]);
80
81        assert!(WSLVersionCapability::try_from(requirement).is_err());
82    }
83
84    proptest! {
85        #[test]
86        fn single_capability_requirement_roundtrips(capability in arb_capability()) {
87            let requirement = RequirementDefinition::from(capability);
88
89            prop_assert_eq!(WSLVersionCapability::try_from(requirement), Ok(capability));
90        }
91    }
92}