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}