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}