Skip to main content

non_empty_str/
boxed.rs

1//! Non-empty [`Box<str>`].
2
3cfg_select! {
4    feature = "std" => {}
5    feature = "alloc" => {
6        use alloc::{boxed::Box, string::String};
7    }
8    _ => {
9        compile_error!("expected either `std` or `alloc` to be enabled");
10    }
11}
12
13use non_empty_iter::{FromNonEmptyIterator, IntoNonEmptyIterator};
14use non_empty_slice::{NonEmptyBoxedBytes, NonEmptyBytes};
15use thiserror::Error;
16
17use crate::{
18    cow::NonEmptyCowStr,
19    str::NonEmptyStr,
20    string::{EmptyString, NonEmptyString},
21};
22
23/// The error message used when the boxed string is empty.
24pub const EMPTY_BOXED_STR: &str = "the boxed string is empty";
25
26/// Similar to [`EmptyString`], but contains the empty boxed string provided.
27#[derive(Debug, Error)]
28#[error("{EMPTY_BOXED_STR}")]
29pub struct EmptyBoxedStr {
30    boxed: Box<str>,
31}
32
33impl EmptyBoxedStr {
34    // NOTE: this is private to prevent creating this error with non-empty boxed strings
35    pub(crate) const fn new(boxed: Box<str>) -> Self {
36        Self { boxed }
37    }
38
39    /// Returns the contained empty boxed string.
40    #[must_use]
41    pub fn get(self) -> Box<str> {
42        self.boxed
43    }
44
45    /// Constructs [`Self`] from [`EmptyString`].
46    #[must_use]
47    pub fn from_empty_string(empty: EmptyString) -> Self {
48        Self::new(empty.get().into_boxed_str())
49    }
50
51    /// Converts [`Self`] into [`EmptyString`].
52    #[must_use]
53    pub fn into_empty_string(self) -> EmptyString {
54        EmptyString::from_empty_boxed_str(self)
55    }
56}
57
58/// Represents non-empty boxed strings, [`Box<NonEmptyStr>`].
59pub type NonEmptyBoxedStr = Box<NonEmptyStr>;
60
61impl Clone for NonEmptyBoxedStr {
62    fn clone(&self) -> Self {
63        self.to_non_empty_string().into_non_empty_boxed_str()
64    }
65}
66
67impl From<NonEmptyBoxedStr> for Box<str> {
68    fn from(boxed: NonEmptyBoxedStr) -> Self {
69        boxed.into_boxed_str()
70    }
71}
72
73impl From<NonEmptyBoxedStr> for Box<[u8]> {
74    fn from(boxed: NonEmptyBoxedStr) -> Self {
75        boxed.into_boxed_bytes()
76    }
77}
78
79impl TryFrom<Box<str>> for NonEmptyBoxedStr {
80    type Error = EmptyBoxedStr;
81
82    fn try_from(boxed: Box<str>) -> Result<Self, Self::Error> {
83        NonEmptyStr::from_boxed_str(boxed)
84    }
85}
86
87impl TryFrom<String> for NonEmptyBoxedStr {
88    type Error = EmptyString;
89
90    fn try_from(string: String) -> Result<Self, Self::Error> {
91        let non_empty_string = NonEmptyString::new(string)?;
92
93        Ok(non_empty_string.into())
94    }
95}
96
97impl From<NonEmptyBoxedStr> for NonEmptyString {
98    fn from(non_empty: NonEmptyBoxedStr) -> Self {
99        non_empty.into_non_empty_string()
100    }
101}
102
103impl From<NonEmptyString> for NonEmptyBoxedStr {
104    fn from(non_empty: NonEmptyString) -> Self {
105        non_empty.into_non_empty_boxed_str()
106    }
107}
108
109impl From<NonEmptyBoxedStr> for String {
110    fn from(non_empty: NonEmptyBoxedStr) -> Self {
111        non_empty.into_boxed_str().into_string()
112    }
113}
114
115impl From<NonEmptyBoxedStr> for NonEmptyBoxedBytes {
116    fn from(non_empty: NonEmptyBoxedStr) -> Self {
117        non_empty.into_non_empty_boxed_bytes()
118    }
119}
120
121impl NonEmptyStr {
122    /// Constructs [`Self`] from [`Box<str>`], provided the boxed string is non-empty.
123    ///
124    /// # Errors
125    ///
126    /// Returns [`EmptyBoxedStr`] if the boxed string is empty.
127    pub fn from_boxed_str(boxed: Box<str>) -> Result<Box<Self>, EmptyBoxedStr> {
128        if boxed.is_empty() {
129            return Err(EmptyBoxedStr::new(boxed));
130        }
131
132        // SAFETY: the boxed string is non-empty at this point
133        Ok(unsafe { Self::from_boxed_str_unchecked(boxed) })
134    }
135
136    /// Constructs [`Self`] from [`Box<str>`] without checking if the boxed string is non-empty.
137    ///
138    /// # Safety
139    ///
140    /// The caller must ensure that the boxed string is non-empty.
141    #[must_use]
142    pub unsafe fn from_boxed_str_unchecked(boxed: Box<str>) -> Box<Self> {
143        // SAFETY: the caller must ensure that the boxed string is non-empty
144        // moreover, `Self` is `repr(transparent)`, so it is safe to transmute
145        // finally, `Box` is created from the raw pointer existing within this function only
146        unsafe { Box::from_raw(Box::into_raw(boxed) as *mut Self) }
147    }
148
149    /// Converts [`Self`] into [`Box<str>`].
150    #[must_use]
151    pub fn into_boxed_str(self: Box<Self>) -> Box<str> {
152        // SAFETY: `Self` is `repr(transparent)`, so it is safe to transmute
153        // moreover, `Box` is created from the raw pointer existing within this function only
154        unsafe { Box::from_raw(Box::into_raw(self) as *mut str) }
155    }
156
157    /// Constructs [`Self`] from [`NonEmptyString`].
158    #[must_use]
159    pub fn from_non_empty_string(non_empty: NonEmptyString) -> Box<Self> {
160        // SAFETY: the string is non-empty by construction, so is the underlying boxed string
161        unsafe { Self::from_boxed_str_unchecked(non_empty.into_string().into_boxed_str()) }
162    }
163
164    /// Converts [`Self`] into [`NonEmptyString`].
165    #[must_use]
166    pub fn into_non_empty_string(self: Box<Self>) -> NonEmptyString {
167        NonEmptyString::from_non_empty_boxed_str(self)
168    }
169
170    /// Converts [`Self`] into [`Box<[u8]>`](Box).
171    #[must_use]
172    pub fn into_boxed_bytes(self: Box<Self>) -> Box<[u8]> {
173        self.into_boxed_str().into_boxed_bytes()
174    }
175
176    /// Converts [`Self`] into [`NonEmptyBoxedBytes`].
177    #[must_use]
178    pub fn into_non_empty_boxed_bytes(self: Box<Self>) -> NonEmptyBoxedBytes {
179        // SAFETY: the string is non-empty by construction, so are its bytes
180        unsafe { NonEmptyBytes::from_boxed_slice_unchecked(self.into_boxed_bytes()) }
181    }
182}
183
184impl NonEmptyString {
185    /// Constructs [`Self`] from [`NonEmptyBoxedStr`].
186    #[must_use]
187    pub fn from_non_empty_boxed_str(non_empty: NonEmptyBoxedStr) -> Self {
188        // SAFETY: the boxed string is non-empty by construction, so is the resulting string
189        unsafe { Self::new_unchecked(non_empty.into_boxed_str().into_string()) }
190    }
191
192    /// Converts [`Self`] into [`NonEmptyBoxedStr`].
193    #[must_use]
194    pub fn into_non_empty_boxed_str(self) -> NonEmptyBoxedStr {
195        NonEmptyStr::from_non_empty_string(self)
196    }
197
198    /// Converts [`Self`] into [`Box<str>`].
199    #[must_use]
200    pub fn into_boxed_str(self) -> Box<str> {
201        self.into_string().into_boxed_str()
202    }
203
204    /// Converts [`Self`] into [`Box<[u8]>`](Box).
205    #[must_use]
206    pub fn into_boxed_bytes(self) -> Box<[u8]> {
207        self.into_non_empty_boxed_str().into_boxed_bytes()
208    }
209
210    /// Converts [`Self`] into [`NonEmptyBoxedBytes`].
211    #[must_use]
212    pub fn into_non_empty_boxed_bytes(self) -> NonEmptyBoxedBytes {
213        self.into_non_empty_boxed_str().into_non_empty_boxed_bytes()
214    }
215}
216
217impl FromNonEmptyIterator<char> for NonEmptyBoxedStr {
218    fn from_non_empty_iter<I: IntoNonEmptyIterator<Item = char>>(iterable: I) -> Self {
219        NonEmptyString::from_non_empty_iter(iterable).into_non_empty_boxed_str()
220    }
221}
222
223impl<'c> FromNonEmptyIterator<&'c char> for NonEmptyBoxedStr {
224    fn from_non_empty_iter<I: IntoNonEmptyIterator<Item = &'c char>>(iterable: I) -> Self {
225        NonEmptyString::from_non_empty_iter(iterable).into_non_empty_boxed_str()
226    }
227}
228
229impl<'s> FromNonEmptyIterator<&'s NonEmptyStr> for NonEmptyBoxedStr {
230    fn from_non_empty_iter<I: IntoNonEmptyIterator<Item = &'s NonEmptyStr>>(iterable: I) -> Self {
231        NonEmptyString::from_non_empty_iter(iterable).into_non_empty_boxed_str()
232    }
233}
234
235impl FromNonEmptyIterator<NonEmptyString> for NonEmptyBoxedStr {
236    fn from_non_empty_iter<I: IntoNonEmptyIterator<Item = NonEmptyString>>(iterable: I) -> Self {
237        NonEmptyString::from_non_empty_iter(iterable).into_non_empty_boxed_str()
238    }
239}
240
241impl FromNonEmptyIterator<Self> for NonEmptyBoxedStr {
242    fn from_non_empty_iter<I: IntoNonEmptyIterator<Item = Self>>(iterable: I) -> Self {
243        NonEmptyString::from_non_empty_iter(iterable).into_non_empty_boxed_str()
244    }
245}
246
247impl<'s> FromNonEmptyIterator<NonEmptyCowStr<'s>> for NonEmptyBoxedStr {
248    fn from_non_empty_iter<I: IntoNonEmptyIterator<Item = NonEmptyCowStr<'s>>>(
249        iterable: I,
250    ) -> Self {
251        NonEmptyString::from_non_empty_iter(iterable).into_non_empty_boxed_str()
252    }
253}