Skip to main content

wslplugins_rs/
user_distribution_id.rs

1use std::{
2    fmt::{Debug, Display, LowerHex, UpperHex},
3    str::FromStr,
4};
5use thiserror::Error;
6pub mod fmt;
7mod parse_error;
8use fmt::DefaultFormatter;
9pub use parse_error::ParseError;
10
11use crate::{CoreWSLDistributionInformation, DistributionID};
12#[cfg(feature = "serde")]
13mod serde_impl;
14#[cfg(feature = "uuid")]
15mod uuid_impl;
16
17#[repr(transparent)]
18/// Identifier for a user-installed WSL distribution.
19///
20/// When the `serde` feature is enabled, human-readable serializers encode this
21/// type as the canonical GUID string. Non-human-readable serializers encode it
22/// as the native 16-byte Windows GUID memory layout for Windows API interop.
23#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
24pub struct UserDistributionID(pub windows_core::GUID);
25
26/// Error type for conversion failures between [`DistributionID`] and [`UserDistributionID`].
27#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
28#[error("Cannot convert System distribution to UserDistribution.")]
29pub struct UserDistributionIDConversionError;
30
31impl From<windows_core::GUID> for UserDistributionID {
32    #[inline]
33    fn from(value: windows_core::GUID) -> Self {
34        Self(value)
35    }
36}
37
38impl From<wslpluginapi_sys::windows_sys::core::GUID> for UserDistributionID {
39    #[inline]
40    fn from(value: wslpluginapi_sys::windows_sys::core::GUID) -> Self {
41        // SAFETY: Representation is the same
42        let guid = unsafe {
43            std::mem::transmute::<wslpluginapi_sys::windows_sys::core::GUID, windows_core::GUID>(
44                value,
45            )
46        };
47        Self(guid)
48    }
49}
50
51impl<T: CoreWSLDistributionInformation> From<&T> for UserDistributionID {
52    /// Converts a reference to a type implementing `CoreWSLDistributionInformation` into a `UserDistributionID`.
53    #[inline]
54    fn from(value: &T) -> Self {
55        value.id()
56    }
57}
58
59impl TryFrom<DistributionID> for UserDistributionID {
60    type Error = UserDistributionIDConversionError;
61    #[inline]
62    fn try_from(value: DistributionID) -> Result<Self, Self::Error> {
63        match value {
64            DistributionID::User(id) => Ok(id),
65            DistributionID::System => Err(UserDistributionIDConversionError),
66        }
67    }
68}
69
70impl From<UserDistributionID> for wslpluginapi_sys::windows_sys::core::GUID {
71    #[inline]
72    fn from(value: UserDistributionID) -> Self {
73        // SAFETY: Representation is the same
74        unsafe { std::mem::transmute(value.0) }
75    }
76}
77
78impl From<UserDistributionID> for windows_core::GUID {
79    #[inline]
80    fn from(value: UserDistributionID) -> Self {
81        value.0
82    }
83}
84
85impl UpperHex for UserDistributionID {
86    #[inline]
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        UpperHex::fmt(&DefaultFormatter::from(*self), f)
89    }
90}
91
92impl LowerHex for UserDistributionID {
93    #[inline]
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        LowerHex::fmt(&DefaultFormatter::from(*self), f)
96    }
97}
98
99impl Debug for UserDistributionID {
100    #[inline]
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        if f.alternate() {
103            f.debug_tuple(stringify!(UserDistributionID))
104                .field(&self.0)
105                .finish()
106        } else {
107            write!(f, "{self}")
108        }
109    }
110}
111
112impl Display for UserDistributionID {
113    #[inline]
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        UpperHex::fmt(&self, f)
116    }
117}
118
119#[cfg(any(windows, feature = "uuid"))]
120impl FromStr for UserDistributionID {
121    type Err = ParseError;
122    #[inline]
123    fn from_str(s: &str) -> Result<Self, Self::Err> {
124        DefaultFormatter::from_str(s).map(Self::from)
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use proptest::prelude::*;
132    proptest! {
133        #[test]
134        fn test_user_distribution_id_formating(int_value in any::<u128>()) {
135            let guid = windows_core::GUID::from_u128(int_value);
136            let user_dist_id: UserDistributionID = guid.into();
137            prop_assert_eq!(format!("{:?}", guid), format!("{:X}", user_dist_id));
138            prop_assert_eq!(format!("{:X}", user_dist_id), format!("{:}", user_dist_id));
139            prop_assert_eq!(format!("{:X}", user_dist_id), format!("{:?}", user_dist_id));
140            prop_assert_eq!(format!("{:x}", user_dist_id), format!("{user_dist_id:X}").to_ascii_lowercase());
141        }
142    }
143
144    #[test]
145    fn try_from_system_returns_error() {
146        assert_eq!(
147            UserDistributionID::try_from(DistributionID::System),
148            Err(UserDistributionIDConversionError)
149        );
150    }
151}