non_empty_str/
owned.rs

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