non_empty_str/
str.rs

1//! Non-empty [`str`].
2
3use core::{fmt, ops::Deref};
4
5use thiserror::Error;
6
7/// The error message used when the string is empty.
8pub const EMPTY: &str = "the string is empty";
9
10/// Represents errors returned when received strings are empty.
11#[derive(Debug, Error)]
12#[error("{EMPTY}")]
13#[cfg_attr(
14    feature = "diagnostics",
15    derive(miette::Diagnostic),
16    diagnostic(code(non_empty_str::str), help("make sure the string is non-empty"))
17)]
18pub struct Empty;
19
20/// Represents non-empty [`str`] values.
21#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
22#[repr(transparent)]
23pub struct Str {
24    inner: str,
25}
26
27impl fmt::Display for Str {
28    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
29        self.get().fmt(formatter)
30    }
31}
32
33#[cfg(any(feature = "std", feature = "alloc"))]
34mod owned {
35    use crate::owned::OwnedStr;
36
37    #[cfg(all(not(feature = "std"), feature = "alloc"))]
38    use alloc::borrow::ToOwned;
39
40    use super::Str;
41
42    impl ToOwned for Str {
43        type Owned = OwnedStr;
44
45        fn to_owned(&self) -> Self::Owned {
46            Self::Owned::from_str(self)
47        }
48    }
49}
50
51impl<'s> TryFrom<&'s str> for &'s Str {
52    type Error = Empty;
53
54    fn try_from(string: &'s str) -> Result<Self, Self::Error> {
55        Str::try_from_str(string)
56    }
57}
58
59impl<'s> From<&'s Str> for &'s str {
60    fn from(string: &'s Str) -> Self {
61        string.get()
62    }
63}
64
65impl AsRef<str> for Str {
66    fn as_ref(&self) -> &str {
67        self.get()
68    }
69}
70
71impl Deref for Str {
72    type Target = str;
73
74    fn deref(&self) -> &Self::Target {
75        self.get()
76    }
77}
78
79impl Str {
80    /// Constructs [`Self`] from anything that can be converted to string, provided it is non-empty.
81    ///
82    /// Prefer [`try_from_str`] if only [`str`] is used, as this allows for `const` evaluation.
83    ///
84    /// # Errors
85    ///
86    /// Returns [`Empty`] if the string is empty.
87    ///
88    /// [`try_from_str`]: Self::try_from_str
89    pub fn try_new<S: AsRef<str> + ?Sized>(string: &S) -> Result<&Self, Empty> {
90        Self::try_from_str(string.as_ref())
91    }
92
93    /// Similar to [`try_new`], but the error is discarded.
94    ///
95    /// Prefer [`from_str`] if only [`str`] is used, as this allows for `const` evaluation.
96    ///
97    /// # Examples
98    ///
99    /// ```
100    /// use non_empty_str::Str;
101    ///
102    /// let non_empty = Str::new("Hello, world!").unwrap();
103    ///
104    /// // `Str` is `AsRef<str>`, so it can also be used!
105    /// let from_non_empty = Str::new(non_empty).unwrap();
106    /// ```
107    ///
108    /// [`try_new`]: Self::try_new
109    /// [`from_str`]: Self::from_str
110    pub fn new<S: AsRef<str> + ?Sized>(string: &S) -> Option<&Self> {
111        Self::from_str(string.as_ref())
112    }
113
114    /// Constructs [`Self`] from anything that can be converted to string, without doing any checks.
115    ///
116    /// Prefer [`from_str_unchecked`] if only [`str`] is used; this allows for `const` evaluation.
117    ///
118    /// # Safety
119    ///
120    /// The caller must ensure that the string is non-empty.
121    ///
122    /// [`from_str_unchecked`]: Self::from_str_unchecked
123    #[must_use]
124    pub unsafe fn new_unchecked<S: AsRef<str> + ?Sized>(string: &S) -> &Self {
125        // SAFETY: the caller must ensure that the string is non-empty
126        unsafe { Self::from_str_unchecked(string.as_ref()) }
127    }
128
129    /// Constructs [`Self`] from [`str`], provided the string is non-empty.
130    ///
131    /// # Errors
132    ///
133    /// Returns [`Empty`] if the string is empty.
134    pub const fn try_from_str(string: &str) -> Result<&Self, Empty> {
135        if string.is_empty() {
136            return Err(Empty);
137        }
138
139        // SAFETY: the string is non-empty at this point
140        Ok(unsafe { Self::from_str_unchecked(string) })
141    }
142
143    /// Similar to [`try_from_str`], but the error is discarded.
144    ///
145    /// # Examples
146    ///
147    /// Basic snippet:
148    ///
149    /// ```
150    /// use non_empty_str::Str;
151    ///
152    /// let message = Str::from_str("Hello, world!").unwrap();
153    /// ```
154    ///
155    /// [`None`] is returned if the string is empty, therefore the following snippet panics:
156    ///
157    /// ```should_panic
158    /// use non_empty_str::Str;
159    ///
160    /// let never = Str::from_str("").unwrap();
161    /// ```
162    ///
163    /// [`try_from_str`]: Self::try_from_str
164    #[must_use]
165    pub const fn from_str(string: &str) -> Option<&Self> {
166        if string.is_empty() {
167            return None;
168        }
169
170        // SAFETY: the string is non-empty at this point
171        Some(unsafe { Self::from_str_unchecked(string) })
172    }
173
174    /// Constructs [`Self`] from [`str`], without checking if the string is empty.
175    ///
176    /// # Safety
177    ///
178    /// The caller must ensure that the string is non-empty.
179    #[must_use]
180    pub const unsafe fn from_str_unchecked(string: &str) -> &Self {
181        debug_assert!(!string.is_empty());
182
183        // SAFETY: the caller must ensure that the string is non-empty
184        // `Str` is `#[repr(transparent)]`, so it is safe to transmute
185        #[allow(clippy::ref_as_ptr)]
186        unsafe {
187            &*(string as *const str as *const Self)
188        }
189    }
190
191    #[cfg(feature = "unsafe-assert")]
192    const fn assert_non_empty(&self) {
193        use core::hint::assert_unchecked;
194
195        // SAFETY: the string is non-empty by construction
196        unsafe {
197            assert_unchecked(!self.inner.is_empty());
198        }
199    }
200
201    /// Returns the contained string.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// use non_empty_str::Str;
207    ///
208    /// let string = "Hello, world!";
209    ///
210    /// let non_empty = Str::from_str(string).unwrap();
211    ///
212    /// assert_eq!(non_empty.get(), string);
213    /// ```
214    #[must_use]
215    pub const fn get(&self) -> &str {
216        #[cfg(feature = "unsafe-assert")]
217        self.assert_non_empty();
218
219        &self.inner
220    }
221}
222
223#[cfg(feature = "serde")]
224mod serde {
225    use super::Str;
226
227    use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
228
229    impl Serialize for Str {
230        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
231            self.get().serialize(serializer)
232        }
233    }
234
235    impl<'de: 's, 's> Deserialize<'de> for &'s Str {
236        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
237            let string = <&str>::deserialize(deserializer)?;
238
239            let non_empty = string.try_into().map_err(D::Error::custom)?;
240
241            Ok(non_empty)
242        }
243    }
244}