winget_types/locale/
installation_notes.rs

1use alloc::string::String;
2use core::{fmt, str::FromStr};
3
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 = "String"))]
9#[repr(transparent)]
10pub struct InstallationNotes(String);
11
12#[derive(Debug, Error, Eq, PartialEq)]
13pub enum InstallationNotesError {
14    #[error("Installation notes must not be empty")]
15    Empty,
16    #[error(
17        "Installation notes must not have more than {} characters but has {_0}",
18        InstallationNotes::MAX_CHAR_LENGTH
19    )]
20    TooLong(usize),
21}
22
23impl InstallationNotes {
24    pub const MAX_CHAR_LENGTH: usize = 10_000;
25
26    /// Creates a new `InstallationNotes` from any type that implements `AsRef<str>` and
27    /// `Into<CompactString>`.
28    ///
29    /// # Errors
30    ///
31    /// Returns an `Err` if the installation notes are empty or more than 10,000 characters long.
32    pub fn new<T: AsRef<str> + Into<String>>(
33        installation_notes: T,
34    ) -> Result<Self, InstallationNotesError> {
35        let notes = installation_notes.as_ref();
36
37        if notes.is_empty() {
38            return Err(InstallationNotesError::Empty);
39        }
40
41        let char_count = notes.chars().count();
42        if char_count > Self::MAX_CHAR_LENGTH {
43            return Err(InstallationNotesError::TooLong(char_count));
44        }
45
46        Ok(Self(installation_notes.into()))
47    }
48
49    /// Creates a new `InstallationNotes` from any type that implements `Into<String>` without
50    /// checking its validity.
51    ///
52    /// # Safety
53    ///
54    /// The installation notes must not be empty or more than 10,000 characters long.
55    #[inline]
56    pub unsafe fn new_unchecked<T: Into<String>>(installation_notes: T) -> Self {
57        Self(installation_notes.into())
58    }
59
60    /// Extracts a string slice containing the entire `InstallationNotes`.
61    #[must_use]
62    #[inline]
63    pub const fn as_str(&self) -> &str {
64        self.0.as_str()
65    }
66}
67
68impl AsRef<str> for InstallationNotes {
69    #[inline]
70    fn as_ref(&self) -> &str {
71        self.as_str()
72    }
73}
74
75impl fmt::Display for InstallationNotes {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        self.0.fmt(f)
78    }
79}
80
81impl FromStr for InstallationNotes {
82    type Err = InstallationNotesError;
83
84    #[inline]
85    fn from_str(s: &str) -> Result<Self, Self::Err> {
86        Self::new(s)
87    }
88}
89
90impl TryFrom<String> for InstallationNotes {
91    type Error = InstallationNotesError;
92
93    #[inline]
94    fn try_from(value: String) -> Result<Self, Self::Error> {
95        Self::new(value)
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use crate::locale::installation_notes::{InstallationNotes, InstallationNotesError};
102
103    #[test]
104    fn valid_installation_notes() {
105        assert!(
106            "Be careful when using this application"
107                .parse::<InstallationNotes>()
108                .is_ok()
109        );
110    }
111
112    #[test]
113    fn installation_notes_max_length() {
114        let installation_notes = "🦀".repeat(InstallationNotes::MAX_CHAR_LENGTH);
115
116        // Ensure that it's character length that's being checked and not byte or UTF-16 length
117        assert!(installation_notes.len() > InstallationNotes::MAX_CHAR_LENGTH);
118        assert!(installation_notes.encode_utf16().count() > InstallationNotes::MAX_CHAR_LENGTH);
119        assert_eq!(
120            installation_notes.chars().count(),
121            InstallationNotes::MAX_CHAR_LENGTH
122        );
123        assert!(InstallationNotes::new(installation_notes).is_ok());
124    }
125
126    #[test]
127    fn installation_notes_too_long() {
128        let installation_notes = "a".repeat(InstallationNotes::MAX_CHAR_LENGTH + 1);
129
130        assert_eq!(
131            installation_notes.parse::<InstallationNotes>(),
132            Err(InstallationNotesError::TooLong(installation_notes.len()))
133        );
134    }
135
136    #[test]
137    fn empty_installation_notes() {
138        assert_eq!(
139            "".parse::<InstallationNotes>(),
140            Err(InstallationNotesError::Empty)
141        );
142    }
143}