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<Self> for Str {
66    fn as_ref(&self) -> &Self {
67        self
68    }
69}
70
71impl AsRef<str> for Str {
72    fn as_ref(&self) -> &str {
73        self.get()
74    }
75}
76
77impl Deref for Str {
78    type Target = str;
79
80    fn deref(&self) -> &Self::Target {
81        self.get()
82    }
83}
84
85impl Str {
86    /// Constructs [`Self`] from anything that can be converted to string, provided it is non-empty.
87    ///
88    /// Prefer [`try_from_str`] if only [`str`] is used, as this allows for `const` evaluation.
89    ///
90    /// # Errors
91    ///
92    /// Returns [`Empty`] if the string is empty.
93    ///
94    /// [`try_from_str`]: Self::try_from_str
95    pub fn try_new<S: AsRef<str> + ?Sized>(string: &S) -> Result<&Self, Empty> {
96        Self::try_from_str(string.as_ref())
97    }
98
99    /// Similar to [`try_new`], but the error is discarded.
100    ///
101    /// Prefer [`from_str`] if only [`str`] is used, as this allows for `const` evaluation.
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// use non_empty_str::Str;
107    ///
108    /// let non_empty = Str::new("Hello, world!").unwrap();
109    ///
110    /// // `Str` is `AsRef<str>`, so it can also be used!
111    /// let from_non_empty = Str::new(non_empty).unwrap();
112    /// ```
113    ///
114    /// [`try_new`]: Self::try_new
115    /// [`from_str`]: Self::from_str
116    pub fn new<S: AsRef<str> + ?Sized>(string: &S) -> Option<&Self> {
117        Self::from_str(string.as_ref())
118    }
119
120    /// Constructs [`Self`] from anything that can be converted to string, without doing any checks.
121    ///
122    /// Prefer [`from_str_unchecked`] if only [`str`] is used; this allows for `const` evaluation.
123    ///
124    /// # Safety
125    ///
126    /// The caller must ensure that the string is non-empty.
127    ///
128    /// [`from_str_unchecked`]: Self::from_str_unchecked
129    #[must_use]
130    pub unsafe fn new_unchecked<S: AsRef<str> + ?Sized>(string: &S) -> &Self {
131        // SAFETY: the caller must ensure that the string is non-empty
132        unsafe { Self::from_str_unchecked(string.as_ref()) }
133    }
134
135    /// Constructs [`Self`] from [`str`], provided the string is non-empty.
136    ///
137    /// # Errors
138    ///
139    /// Returns [`Empty`] if the string is empty.
140    pub const fn try_from_str(string: &str) -> Result<&Self, Empty> {
141        if string.is_empty() {
142            return Err(Empty);
143        }
144
145        // SAFETY: the string is non-empty at this point
146        Ok(unsafe { Self::from_str_unchecked(string) })
147    }
148
149    /// Similar to [`try_from_str`], but the error is discarded.
150    ///
151    /// # Examples
152    ///
153    /// Basic snippet:
154    ///
155    /// ```
156    /// use non_empty_str::Str;
157    ///
158    /// let message = Str::from_str("Hello, world!").unwrap();
159    /// ```
160    ///
161    /// [`None`] is returned if the string is empty, therefore the following snippet panics:
162    ///
163    /// ```should_panic
164    /// use non_empty_str::Str;
165    ///
166    /// let never = Str::from_str("").unwrap();
167    /// ```
168    ///
169    /// [`try_from_str`]: Self::try_from_str
170    #[must_use]
171    pub const fn from_str(string: &str) -> Option<&Self> {
172        if string.is_empty() {
173            return None;
174        }
175
176        // SAFETY: the string is non-empty at this point
177        Some(unsafe { Self::from_str_unchecked(string) })
178    }
179
180    /// Constructs [`Self`] from [`str`], without checking if the string is empty.
181    ///
182    /// # Safety
183    ///
184    /// The caller must ensure that the string is non-empty.
185    #[must_use]
186    pub const unsafe fn from_str_unchecked(string: &str) -> &Self {
187        debug_assert!(!string.is_empty());
188
189        // SAFETY: the caller must ensure that the string is non-empty
190        // `Str` is `#[repr(transparent)]`, so it is safe to transmute
191        #[allow(clippy::ref_as_ptr)]
192        unsafe {
193            &*(string as *const str as *const Self)
194        }
195    }
196
197    #[cfg(feature = "unsafe-assert")]
198    const fn assert_non_empty(&self) {
199        use core::hint::assert_unchecked;
200
201        // SAFETY: the string is non-empty by construction
202        unsafe {
203            assert_unchecked(!self.inner.is_empty());
204        }
205    }
206
207    /// Returns the contained string.
208    ///
209    /// # Examples
210    ///
211    /// ```
212    /// use non_empty_str::Str;
213    ///
214    /// let string = "Hello, world!";
215    ///
216    /// let non_empty = Str::from_str(string).unwrap();
217    ///
218    /// assert_eq!(non_empty.get(), string);
219    /// ```
220    #[must_use]
221    pub const fn get(&self) -> &str {
222        #[cfg(feature = "unsafe-assert")]
223        self.assert_non_empty();
224
225        &self.inner
226    }
227}
228
229#[cfg(feature = "serde")]
230mod serde {
231    use super::Str;
232
233    use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
234
235    impl Serialize for Str {
236        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
237            self.get().serialize(serializer)
238        }
239    }
240
241    impl<'de: 's, 's> Deserialize<'de> for &'s Str {
242        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
243            let string = <&str>::deserialize(deserializer)?;
244
245            let non_empty = string.try_into().map_err(D::Error::custom)?;
246
247            Ok(non_empty)
248        }
249    }
250}