uncased/
borrowed.rs
1use core::cmp::Ordering;
2use core::hash::{Hash, Hasher};
3use core::fmt;
4
5#[cfg(feature = "alloc")]
6use alloc::{string::String, sync::Arc};
7
8#[derive(Debug)]
19#[repr(transparent)]
20pub struct UncasedStr(str);
21
22impl UncasedStr {
23 #[inline(always)]
38 #[cfg(not(const_fn_transmute))]
39 pub fn new(string: &str) -> &UncasedStr {
40 unsafe { &*(string as *const str as *const UncasedStr) }
43 }
44
45 #[inline(always)]
60 #[cfg(const_fn_transmute)]
61 pub const fn new(string: &str) -> &UncasedStr {
62 unsafe { core::mem::transmute(string) }
65 }
66
67 #[inline(always)]
79 pub fn as_str(&self) -> &str {
80 &self.0
81 }
82
83 #[inline(always)]
94 pub fn len(&self) -> usize {
95 self.as_str().len()
96 }
97
98 #[inline]
112 pub fn is_empty(&self) -> bool {
113 self.as_str().is_empty()
114 }
115
116 #[inline(always)]
138 pub fn starts_with(&self, string: &str) -> bool {
139 self.as_str()
140 .get(..string.len())
141 .map(|s| Self::new(s) == string)
142 .unwrap_or(false)
143 }
144
145 #[inline(always)]
158 #[cfg(feature = "alloc")]
159 #[cfg_attr(nightly, doc(cfg(feature = "alloc")))]
160 pub fn into_uncased(self: alloc::boxed::Box<UncasedStr>) -> crate::Uncased<'static> {
161 unsafe {
164 let raw_str = alloc::boxed::Box::into_raw(self) as *mut str;
165 crate::Uncased::from(alloc::boxed::Box::from_raw(raw_str).into_string())
166 }
167 }
168}
169
170impl<'a> From<&'a str> for &'a UncasedStr {
171 #[inline(always)]
172 fn from(string: &'a str) -> &'a UncasedStr {
173 UncasedStr::new(string)
174 }
175}
176
177impl<I: core::slice::SliceIndex<str, Output = str>> core::ops::Index<I> for UncasedStr {
178 type Output = UncasedStr;
179
180 #[inline]
181 fn index(&self, index: I) -> &Self::Output {
182 self.as_str()[index].into()
183 }
184}
185
186impl AsRef<str> for UncasedStr {
187 fn as_ref(&self) -> &str {
188 self.as_str()
189 }
190}
191
192impl AsRef<[u8]> for UncasedStr {
193 fn as_ref(&self) -> &[u8] {
194 self.as_str().as_bytes()
195 }
196}
197
198impl fmt::Display for UncasedStr {
199 #[inline(always)]
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 self.0.fmt(f)
202 }
203}
204
205macro_rules! impl_partial_eq {
206 ($other:ty $([$o_i:ident])? = $this:ty $([$t_i:ident])?) => (
207 impl PartialEq<$other> for $this {
208 #[inline(always)]
209 fn eq(&self, other: &$other) -> bool {
210 self $(.$t_i())? .eq_ignore_ascii_case(other $(.$o_i())?)
211 }
212 }
213 )
214}
215
216impl_partial_eq!(UncasedStr [as_str] = UncasedStr [as_str]);
217impl_partial_eq!(str = UncasedStr [as_str]);
218impl_partial_eq!(UncasedStr [as_str] = str);
219impl_partial_eq!(str = &UncasedStr [as_str]);
220impl_partial_eq!(&UncasedStr [as_str] = str);
221impl_partial_eq!(&str = UncasedStr [as_str]);
222impl_partial_eq!(UncasedStr [as_str] = &str);
223
224#[cfg(feature = "alloc")] impl_partial_eq!(String [as_str] = UncasedStr [as_str]);
225
226#[cfg(feature = "alloc")] impl_partial_eq!(UncasedStr [as_str] = String [as_str] );
227
228impl Eq for UncasedStr { }
229
230macro_rules! impl_partial_ord {
231 ($other:ty $([$o_i:ident])? >< $this:ty $([$t_i:ident])?) => (
232 impl PartialOrd<$other> for $this {
233 #[inline(always)]
234 fn partial_cmp(&self, other: &$other) -> Option<Ordering> {
235 let this: &UncasedStr = self$(.$t_i())?.into();
236 let other: &UncasedStr = other$(.$o_i())?.into();
237 this.partial_cmp(other)
238 }
239 }
240 )
241}
242
243impl PartialOrd for UncasedStr {
244 #[inline(always)]
245 fn partial_cmp(&self, other: &UncasedStr) -> Option<Ordering> {
246 Some(self.cmp(other))
247 }
248}
249
250impl Ord for UncasedStr {
251 fn cmp(&self, other: &Self) -> Ordering {
252 let self_chars = self.0.chars().map(|c| c.to_ascii_lowercase());
253 let other_chars = other.0.chars().map(|c| c.to_ascii_lowercase());
254 self_chars.cmp(other_chars)
255 }
256}
257
258impl_partial_ord!(str >< UncasedStr);
259impl_partial_ord!(UncasedStr >< str);
260
261#[cfg(feature = "alloc")] impl_partial_ord!(String [as_str] >< UncasedStr);
262#[cfg(feature = "alloc")] impl_partial_ord!(UncasedStr >< String [as_str]);
263
264impl Hash for UncasedStr {
265 #[inline(always)]
266 fn hash<H: Hasher>(&self, hasher: &mut H) {
267 self.0.bytes().for_each(|b| hasher.write_u8(b.to_ascii_lowercase()));
268 }
269}
270
271#[cfg(feature = "alloc")]
272impl From<&UncasedStr> for Arc<UncasedStr> {
273 #[inline]
274 fn from(v: &UncasedStr) -> Arc<UncasedStr> {
275 let arc: Arc<str> = Arc::from(&v.0);
280 let raw = Arc::into_raw(arc) as *const str as *const UncasedStr;
281 unsafe { Arc::from_raw(raw) }
282 }
283}