uncased/
owned.rs

1use alloc::borrow::{Cow, Borrow};
2use alloc::{string::String, boxed::Box};
3
4use core::ops::Deref;
5use core::cmp::Ordering;
6use core::hash::{Hash, Hasher};
7use core::fmt;
8
9use crate::{UncasedStr, AsUncased};
10
11/// An uncased (case-insensitive, case-preserving), owned _or_ borrowed ASCII
12/// string.
13#[cfg_attr(nightly, doc(cfg(feature = "alloc")))]
14#[derive(Clone, Debug)]
15pub struct Uncased<'s> {
16    #[doc(hidden)]
17    pub string: Cow<'s, str>
18}
19
20impl<'s> Uncased<'s> {
21    /// Creates a new `Uncased` string from `string` without allocating.
22    ///
23    /// # Example
24    ///
25    /// ```rust
26    /// use uncased::Uncased;
27    ///
28    /// let uncased = Uncased::new("Content-Type");
29    /// assert_eq!(uncased, "content-type");
30    /// assert_eq!(uncased, "CONTENT-Type");
31    /// ```
32    #[inline(always)]
33    pub fn new<S: Into<Cow<'s, str>>>(string: S) -> Uncased<'s> {
34        Uncased { string: string.into() }
35    }
36
37    /// Creates a new `Uncased` string from a borrowed `string`.
38    ///
39    /// # Example
40    ///
41    /// ```rust
42    /// use uncased::Uncased;
43    ///
44    /// const UNCASED: Uncased = Uncased::from_borrowed("Content-Type");
45    /// assert_eq!(UNCASED, "content-type");
46    /// assert_eq!(UNCASED, "CONTENT-Type");
47    /// ```
48    #[inline(always)]
49    pub const fn from_borrowed(string: &'s str) -> Uncased<'s> {
50        Uncased { string: Cow::Borrowed(string) }
51    }
52
53    /// Creates a new `Uncased` string from `string` without allocating.
54    ///
55    /// # Example
56    ///
57    /// ```rust
58    /// use uncased::Uncased;
59    ///
60    /// const UNCASED: Uncased = Uncased::from_owned(String::new());
61    ///
62    /// let uncased = Uncased::from_owned("Content-Type".to_string());
63    /// assert_eq!(uncased, "content-type");
64    /// assert_eq!(uncased, "CONTENT-Type");
65    /// ```
66    #[inline(always)]
67    pub const fn from_owned(string: String) -> Uncased<'s> {
68        Uncased { string: Cow::Owned(string) }
69    }
70
71    /// Converts `self` into an owned `Uncased<'static>`, allocating if
72    /// necessary.
73    ///
74    /// # Example
75    ///
76    /// ```rust
77    /// use uncased::Uncased;
78    ///
79    /// let foo = "foo".to_string();
80    /// let uncased = Uncased::from(foo.as_str());
81    /// let owned: Uncased<'static> = uncased.into_owned();
82    /// assert_eq!(owned, "foo");
83    /// ```
84    #[inline(always)]
85    pub fn as_uncased_str(&self) -> &UncasedStr {
86        self.as_ref()
87    }
88
89    /// Converts `self` into an owned `String`, allocating if necessary.
90    ///
91    /// # Example
92    ///
93    /// ```rust
94    /// use uncased::Uncased;
95    ///
96    /// let uncased = Uncased::new("Content-Type");
97    /// let string = uncased.into_string();
98    /// assert_eq!(string, "Content-Type".to_string());
99    /// ```
100    #[inline(always)]
101    pub fn into_string(self) -> String {
102        self.string.into_owned()
103    }
104
105    /// Converts `self` into an owned `Uncased<'static>`, allocating if
106    /// necessary.
107    ///
108    /// # Example
109    ///
110    /// ```rust
111    /// use uncased::Uncased;
112    ///
113    /// let foo = "foo".to_string();
114    /// let uncased = Uncased::from(foo.as_str());
115    /// let owned: Uncased<'static> = uncased.into_owned();
116    /// assert_eq!(owned, "foo");
117    /// ```
118    #[inline(always)]
119    pub fn into_owned(self) -> Uncased<'static> {
120        match self.string {
121            Cow::Owned(string) => Uncased::new(string),
122            Cow::Borrowed(str) => Uncased::new(String::from(str)),
123        }
124    }
125
126    /// Converts `self` into a `Box<UncasedStr>`.
127    ///
128    /// # Example
129    ///
130    /// ```rust
131    /// use uncased::Uncased;
132    ///
133    /// let boxed = Uncased::new("Content-Type").into_boxed_uncased();
134    /// assert_eq!(&*boxed, "content-type");
135    /// ```
136    #[inline(always)]
137    pub fn into_boxed_uncased(self) -> Box<UncasedStr> {
138        // This is simply a `newtype`-like transformation. The
139        // `repr(transparent)` ensures that this is safe and correct. Note this
140        // exact pattern appears often in the standard library.
141        unsafe {
142            let raw_str = Box::into_raw(self.string.into_owned().into_boxed_str());
143            Box::from_raw(raw_str as *mut UncasedStr)
144        }
145    }
146
147    /// Returns the inner `Cow`.
148    #[doc(hidden)]
149    #[inline(always)]
150    pub fn into_cow(self) -> Cow<'s, str> {
151        self.string
152    }
153}
154
155impl Deref for Uncased<'_> {
156    type Target = UncasedStr;
157
158    #[inline(always)]
159    fn deref(&self) -> &UncasedStr {
160        UncasedStr::new(self.string.borrow())
161    }
162}
163
164impl AsRef<UncasedStr> for Uncased<'_> {
165    #[inline(always)]
166    fn as_ref(&self) -> &UncasedStr {
167        UncasedStr::new(self.string.borrow())
168    }
169}
170
171impl AsRef<str> for Uncased<'_> {
172    #[inline(always)]
173    fn as_ref(&self) -> &str {
174        self.as_str()
175    }
176}
177
178impl AsRef<[u8]> for Uncased<'_> {
179    #[inline(always)]
180    fn as_ref(&self) -> &[u8] {
181        self.as_str().as_bytes()
182    }
183}
184
185impl Borrow<UncasedStr> for Uncased<'_> {
186    #[inline(always)]
187    fn borrow(&self) -> &UncasedStr {
188        self.as_str().into()
189    }
190}
191
192impl<'s, 'c: 's> From<&'c UncasedStr> for Uncased<'s> {
193    #[inline(always)]
194    fn from(string: &'c UncasedStr) -> Self {
195        Uncased::new(string.as_str())
196    }
197}
198
199impl<'s, 'c: 's> From<&'c str> for Uncased<'s> {
200    #[inline(always)]
201    fn from(string: &'c str) -> Self {
202        Uncased::new(string)
203    }
204}
205
206impl From<String> for Uncased<'static> {
207    #[inline(always)]
208    fn from(string: String) -> Self {
209        Uncased::new(string)
210    }
211}
212
213impl<'s, 'c: 's> From<Cow<'c, str>> for Uncased<'s> {
214    #[inline(always)]
215    fn from(string: Cow<'c, str>) -> Self {
216        Uncased::new(string)
217    }
218}
219
220impl fmt::Display for Uncased<'_> {
221    #[inline(always)]
222    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223        self.string.fmt(f)
224    }
225}
226
227macro_rules! impl_partial_eq {
228    ($other:ty $([$o_i:ident])? = $this:ty $([$t_i:ident])?) => (
229        impl PartialEq<$other> for $this {
230            #[inline(always)]
231            fn eq(&self, other: &$other) -> bool {
232                let this: &UncasedStr = self$(.$t_i())?;
233                let other: &UncasedStr = other$(.$o_i())?;
234                this.eq(other)
235            }
236        }
237    )
238}
239
240impl_partial_eq!(Uncased<'_> [as_uncased_str] = Uncased<'_> [as_uncased_str]);
241impl_partial_eq!(str [as_uncased] = Uncased<'_> [as_uncased_str]);
242impl_partial_eq!(Uncased<'_> [as_uncased_str] = str [as_uncased]);
243impl_partial_eq!(&str [as_uncased] = Uncased<'_> [as_uncased_str]);
244impl_partial_eq!(Uncased<'_> [as_uncased_str] = &str [as_uncased]);
245impl_partial_eq!(String [as_uncased] = Uncased<'_> [as_uncased_str]);
246impl_partial_eq!(Uncased<'_> [as_uncased_str] = String [as_uncased]);
247impl_partial_eq!(String [as_uncased] = &Uncased<'_> [as_uncased_str]);
248impl_partial_eq!(&Uncased<'_> [as_uncased_str] = String [as_uncased]);
249impl_partial_eq!(UncasedStr = Uncased<'_> [as_uncased_str]);
250impl_partial_eq!(&UncasedStr = Uncased<'_> [as_uncased_str]);
251impl_partial_eq!(Uncased<'_> [as_uncased_str] = UncasedStr);
252impl_partial_eq!(Uncased<'_> [as_uncased_str] = &UncasedStr);
253impl_partial_eq!(UncasedStr = &Uncased<'_> [as_uncased_str]);
254impl_partial_eq!(&Uncased<'_> [as_uncased_str] = UncasedStr);
255
256macro_rules! impl_partial_ord {
257    ($other:ty $([$o_i:ident])? >< $this:ty $([$t_i:ident])?) => (
258        impl PartialOrd<$other> for $this {
259            #[inline(always)]
260            fn partial_cmp(&self, other: &$other) -> Option<Ordering> {
261                let this: &UncasedStr = self$(.$t_i())?;
262                let other: &UncasedStr = other$(.$o_i())?;
263                this.partial_cmp(other)
264            }
265        }
266    )
267}
268
269impl Ord for Uncased<'_> {
270    fn cmp(&self, other: &Self) -> Ordering {
271        self.as_uncased_str().cmp(other.as_uncased_str())
272    }
273}
274
275impl_partial_ord!(Uncased<'_> [as_uncased_str] >< Uncased<'_> [as_uncased_str]);
276impl_partial_ord!(str [as_uncased] >< Uncased<'_> [as_uncased_str]);
277impl_partial_ord!(Uncased<'_> [as_uncased_str] >< str [as_uncased]);
278impl_partial_ord!(String [as_uncased] >< Uncased<'_> [as_uncased_str]);
279impl_partial_ord!(Uncased<'_> [as_uncased_str] >< String [as_uncased]);
280impl_partial_ord!(String [as_uncased] >< &Uncased<'_> [as_uncased_str]);
281impl_partial_ord!(&Uncased<'_> [as_uncased_str] >< String [as_uncased]);
282impl_partial_ord!(UncasedStr >< Uncased<'_> [as_uncased_str]);
283impl_partial_ord!(&UncasedStr >< Uncased<'_> [as_uncased_str]);
284impl_partial_ord!(Uncased<'_> [as_uncased_str] >< UncasedStr);
285impl_partial_ord!(UncasedStr >< &Uncased<'_> [as_uncased_str]);
286impl_partial_ord!(Uncased<'_> [as_uncased_str] >< &UncasedStr);
287impl_partial_ord!(&Uncased<'_> [as_uncased_str] >< UncasedStr);
288
289impl Eq for Uncased<'_> {  }
290
291impl Hash for Uncased<'_> {
292    #[inline(always)]
293    fn hash<H: Hasher>(&self, hasher: &mut H) {
294        self.as_uncased_str().hash(hasher)
295    }
296}