non_empty_str/
cow.rs

1//! Non-empty [`Cow<'_, str>`].
2//!
3//! [`Cow<'_, str>`]: Cow
4
5#[cfg(feature = "unsafe-assert")]
6use core::hint::assert_unchecked;
7
8use core::{fmt, ops::Deref};
9
10#[cfg(feature = "std")]
11use std::borrow::Cow;
12
13#[cfg(all(not(feature = "std"), feature = "alloc"))]
14use alloc::{borrow::Cow, string::String};
15
16#[cfg(all(
17    not(feature = "std"),
18    feature = "alloc",
19    feature = "serde",
20    feature = "borrow"
21))]
22use alloc::borrow::ToOwned;
23
24use const_macros::{const_none, const_ok, const_try};
25
26#[cfg(feature = "serde")]
27use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
28
29#[cfg(all(feature = "serde", feature = "borrow"))]
30use serde::de::Visitor;
31
32use crate::{
33    empty::{Empty, check_str},
34    str::Str,
35};
36
37/// Represents non-empty clone-on-write strings.
38#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
39pub struct CowStr<'s> {
40    value: Cow<'s, str>,
41}
42
43#[cfg(feature = "serde")]
44impl Serialize for CowStr<'_> {
45    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
46        self.get().serialize(serializer)
47    }
48}
49
50#[cfg(all(feature = "serde", feature = "borrow"))]
51struct CowStrVisitor;
52
53#[cfg(all(feature = "serde", feature = "borrow"))]
54impl<'de> Visitor<'de> for CowStrVisitor {
55    type Value = CowStr<'de>;
56
57    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
58        formatter.write_str("non-empty string")
59    }
60
61    fn visit_borrowed_str<E: Error>(self, value: &'de str) -> Result<Self::Value, E> {
62        CowStr::borrowed(value).map_err(Error::custom)
63    }
64
65    fn visit_str<E: Error>(self, value: &str) -> Result<Self::Value, E> {
66        self.visit_string(value.to_owned())
67    }
68
69    fn visit_string<E: Error>(self, value: String) -> Result<Self::Value, E> {
70        CowStr::owned(value).map_err(Error::custom)
71    }
72}
73
74#[cfg(all(feature = "serde", feature = "borrow"))]
75impl<'de: 'c, 'c> Deserialize<'de> for CowStr<'c> {
76    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
77        deserializer.deserialize_str(CowStrVisitor)
78    }
79}
80
81#[cfg(all(feature = "serde", not(feature = "borrow")))]
82impl<'de> Deserialize<'de> for CowStr<'_> {
83    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
84        let value = Cow::deserialize(deserializer)?;
85
86        Self::new(value).map_err(Error::custom)
87    }
88}
89
90impl fmt::Display for CowStr<'_> {
91    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
92        self.get().fmt(formatter)
93    }
94}
95
96impl AsRef<str> for CowStr<'_> {
97    fn as_ref(&self) -> &str {
98        self.get()
99    }
100}
101
102impl Deref for CowStr<'_> {
103    type Target = str;
104
105    fn deref(&self) -> &Self::Target {
106        self.get()
107    }
108}
109
110impl<'s> TryFrom<Cow<'s, str>> for CowStr<'s> {
111    type Error = Empty;
112
113    fn try_from(value: Cow<'s, str>) -> Result<Self, Self::Error> {
114        Self::new(value)
115    }
116}
117
118impl<'s> TryFrom<&'s str> for CowStr<'s> {
119    type Error = Empty;
120
121    fn try_from(value: &'s str) -> Result<Self, Self::Error> {
122        Self::borrowed(value)
123    }
124}
125
126impl TryFrom<String> for CowStr<'_> {
127    type Error = Empty;
128
129    fn try_from(value: String) -> Result<Self, Self::Error> {
130        Self::owned(value)
131    }
132}
133
134impl<'s> From<Str<'s>> for CowStr<'s> {
135    fn from(string: Str<'s>) -> Self {
136        Self::from_str(string)
137    }
138}
139
140impl<'s> From<CowStr<'s>> for Cow<'s, str> {
141    fn from(value: CowStr<'s>) -> Self {
142        value.take()
143    }
144}
145
146impl<'s> CowStr<'s> {
147    /// Constructs [`Self`], provided that the value is non-empty.
148    ///
149    /// # Errors
150    ///
151    /// Returns [`Empty`] if the string is empty.
152    pub fn new(value: Cow<'s, str>) -> Result<Self, Empty> {
153        const_try!(check_str(value.as_ref()));
154
155        Ok(unsafe { Self::new_unchecked(value) })
156    }
157
158    /// Similar to [`new`], except the error is discarded.
159    ///
160    /// [`new`]: Self::new
161    pub fn new_ok(value: Cow<'s, str>) -> Option<Self> {
162        const_ok!(Self::new(value))
163    }
164
165    /// Constructs [`Self`] without checking if the value is non-empty.
166    ///
167    /// # Safety
168    ///
169    /// The caller must ensure that the value is non-empty.
170    pub const unsafe fn new_unchecked(value: Cow<'s, str>) -> Self {
171        Self { value }
172    }
173
174    /// Similar to [`new`], but accepts borrowed strings.
175    ///
176    /// # Errors
177    ///
178    /// Returns [`Empty`] if the string is empty.
179    ///
180    /// [`new`]: Self::new
181    pub const fn borrowed(value: &'s str) -> Result<Self, Empty> {
182        const_try!(check_str(value));
183
184        // SAFETY: the value is non-empty at this point
185        Ok(unsafe { Self::borrowed_unchecked(value) })
186    }
187
188    /// Similar to [`borrowed`], but the error is discarded.
189    ///
190    /// [`borrowed`]: Self::borrowed
191    pub const fn borrowed_ok(value: &'s str) -> Option<Self> {
192        // NOTE: we can not use `const_ok!(Self::borrowed(value))` currently
193
194        const_none!(const_ok!(check_str(value)));
195
196        Some(unsafe { Self::borrowed_unchecked(value) })
197    }
198
199    /// Similar to [`new_unchecked`], but accepts borrowed strings.
200    ///
201    /// # Safety
202    ///
203    /// The caller must ensure that the value is non-empty.
204    ///
205    /// [`new_unchecked`]: Self::new_unchecked
206    pub const unsafe fn borrowed_unchecked(value: &'s str) -> Self {
207        // SAFETY: the caller must ensure that the value is non-empty
208        unsafe { Self::new_unchecked(Cow::Borrowed(value)) }
209    }
210
211    /// Similar to [`new`], but accepts owned strings.
212    ///
213    /// # Errors
214    ///
215    /// Returns [`Empty`] if the string is empty.
216    ///
217    /// [`new`]: Self::new
218    pub fn owned(value: String) -> Result<Self, Empty> {
219        const_try!(check_str(value.as_str()));
220
221        // SAFETY: the value is non-empty at this point
222        Ok(unsafe { Self::owned_unchecked(value) })
223    }
224
225    /// Similar to [`owned`], except the error is discarded.
226    ///
227    /// [`owned`]: Self::owned
228    pub fn owned_ok(value: String) -> Option<Self> {
229        const_ok!(Self::owned(value))
230    }
231
232    /// Similar to [`new_unchecked`], but accepts owned strings.
233    ///
234    /// # Safety
235    ///
236    /// The caller must ensure that the value is non-empty.
237    ///
238    /// [`new_unchecked`]: Self::new_unchecked
239    pub const unsafe fn owned_unchecked(value: String) -> Self {
240        unsafe { Self::new_unchecked(Cow::Owned(value)) }
241    }
242
243    #[cfg(feature = "unsafe-assert")]
244    fn assert_non_empty(&self) {
245        unsafe { assert_unchecked(!self.value.is_empty()) }
246    }
247
248    /// Consumes [`Self`] and returns the wrapped string.
249    pub fn take(self) -> Cow<'s, str> {
250        #[cfg(feature = "unsafe-assert")]
251        self.assert_non_empty();
252
253        self.value
254    }
255
256    /// Constructs [`Self`] from [`Str`].
257    pub const fn from_str(string: Str<'s>) -> Self {
258        // SAFETY: the contained string is non-empty
259        unsafe { Self::borrowed_unchecked(string.take()) }
260    }
261}
262
263/// Type alias for [`CowStr`] with `'static` lifetime.
264pub type StaticCowStr = CowStr<'static>;
265
266impl CowStr<'_> {
267    /// Returns the wrapped string reference.
268    pub fn get(&self) -> &str {
269        #[cfg(feature = "unsafe-assert")]
270        self.assert_non_empty();
271
272        self.value.as_ref()
273    }
274
275    /// Converts [`Self`] into [`StaticCowStr`].
276    pub fn into_static(self) -> StaticCowStr {
277        // SAFETY: the contained string is non-empty
278        unsafe { StaticCowStr::owned_unchecked(self.take().into_owned()) }
279    }
280}