winget_types/locale/
package_name.rs

1use core::{fmt, str::FromStr};
2
3use compact_str::CompactString;
4use thiserror::Error;
5
6#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[cfg_attr(feature = "serde", serde(try_from = "CompactString"))]
9#[repr(transparent)]
10pub struct PackageName(CompactString);
11
12#[derive(Debug, Error, Eq, PartialEq)]
13pub enum PackageNameError {
14    #[error(
15        "Package name must have at least {} characters but has {_0}",
16        PackageName::MIN_CHAR_LENGTH
17    )]
18    TooShort(usize),
19    #[error(
20        "Package name must not have more than {} characters but has {_0}",
21        PackageName::MAX_CHAR_LENGTH
22    )]
23    TooLong(usize),
24}
25
26impl PackageName {
27    pub const MIN_CHAR_LENGTH: usize = 2;
28    pub const MAX_CHAR_LENGTH: usize = 256;
29
30    /// Creates a new `PackageName` from any type that implements `AsRef<str>` and
31    /// `Into<CompactString>`.
32    ///
33    /// # Errors
34    ///
35    /// Returns an `Err` if the package name is less than 2 characters long or more than 256
36    /// characters long.
37    ///
38    /// # Examples
39    ///
40    /// ```
41    /// use winget_types::locale::PackageName;
42    /// # use winget_types::locale::PackageNameError;
43    ///
44    /// # fn main() -> Result<(), PackageNameError>  {
45    /// let package_name = PackageName::new("PowerShell")?;
46    ///
47    /// assert_eq!(package_name.as_str(), "PowerShell");
48    /// # Ok(())
49    /// # }
50    /// ```
51    pub fn new<T: AsRef<str> + Into<CompactString>>(
52        package_name: T,
53    ) -> Result<Self, PackageNameError> {
54        match package_name.as_ref().chars().count() {
55            count if count < Self::MIN_CHAR_LENGTH => Err(PackageNameError::TooShort(count)),
56            count if count > Self::MAX_CHAR_LENGTH => Err(PackageNameError::TooLong(count)),
57            _ => Ok(Self(package_name.into())),
58        }
59    }
60
61    /// Creates a new `PackageName` from any type that implements `Into<CompactString>` without
62    /// checking its validity.
63    ///
64    /// # Safety
65    ///
66    /// The package name must not be less than 2 characters long or more than 256 characters long.
67    #[must_use]
68    #[inline]
69    pub unsafe fn new_unchecked<T: Into<CompactString>>(package_name: T) -> Self {
70        Self(package_name.into())
71    }
72
73    /// Extracts a string slice containing the entire `PackageName`.
74    #[must_use]
75    #[inline]
76    pub fn as_str(&self) -> &str {
77        self.0.as_str()
78    }
79}
80
81impl AsRef<str> for PackageName {
82    #[inline]
83    fn as_ref(&self) -> &str {
84        self.as_str()
85    }
86}
87
88impl Default for PackageName {
89    fn default() -> Self {
90        // SAFETY: `Package name` is longer than 2 characters
91        unsafe { Self::new_unchecked("Package name") }
92    }
93}
94
95impl fmt::Display for PackageName {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        self.0.fmt(f)
98    }
99}
100
101impl FromStr for PackageName {
102    type Err = PackageNameError;
103
104    #[inline]
105    fn from_str(s: &str) -> Result<Self, Self::Err> {
106        Self::new(s)
107    }
108}
109
110impl TryFrom<CompactString> for PackageName {
111    type Error = PackageNameError;
112
113    #[inline]
114    fn try_from(value: CompactString) -> Result<Self, Self::Error> {
115        Self::new(value)
116    }
117}