unicase/
ascii.rs

1use alloc::string::String;
2use core::cmp::Ordering;
3use core::fmt;
4use core::hash::{Hash, Hasher};
5use core::ops::{Deref, DerefMut};
6use core::str::FromStr;
7
8use super::{Ascii, Encoding, UniCase};
9
10impl<S> Ascii<S> {
11    /// Construct a new `Ascii`.
12    ///
13    /// For Rust versions >= 1.31, this is a `const fn`.
14    #[inline]
15    pub const fn new(s: S) -> Ascii<S> {
16        Ascii(s)
17    }
18
19    /// Convert this into a [`UniCase`].
20    pub const fn into_unicase(self) -> UniCase<S> {
21        UniCase(Encoding::Ascii(self))
22    }
23
24    /// Consume this `Ascii` and get the inner value.
25    #[inline]
26    pub fn into_inner(self) -> S {
27        self.0
28    }
29}
30
31impl<S> Deref for Ascii<S> {
32    type Target = S;
33    #[inline]
34    fn deref<'a>(&'a self) -> &'a S {
35        &self.0
36    }
37}
38
39impl<S> DerefMut for Ascii<S> {
40    #[inline]
41    fn deref_mut<'a>(&'a mut self) -> &'a mut S {
42        &mut self.0
43    }
44}
45
46impl<T: AsRef<str>> PartialOrd for Ascii<T> {
47    #[inline]
48    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
49        Some(self.cmp(other))
50    }
51}
52
53impl<T: AsRef<str>> Ord for Ascii<T> {
54    #[inline]
55    fn cmp(&self, other: &Self) -> Ordering {
56        let self_chars = self.as_ref().chars().map(|c| c.to_ascii_lowercase());
57        let other_chars = other.as_ref().chars().map(|c| c.to_ascii_lowercase());
58        self_chars.cmp(other_chars)
59    }
60}
61
62impl<S: AsRef<str>> AsRef<str> for Ascii<S> {
63    #[inline]
64    fn as_ref(&self) -> &str {
65        self.0.as_ref()
66    }
67}
68
69impl<S: fmt::Display> fmt::Display for Ascii<S> {
70    #[inline]
71    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
72        fmt::Display::fmt(&self.0, fmt)
73    }
74}
75
76impl<S1: AsRef<str>> PartialEq<Ascii<S1>> for String {
77    #[inline]
78    fn eq(&self, other: &Ascii<S1>) -> bool {
79        other == self
80    }
81}
82
83impl<'a, S1: AsRef<str>> PartialEq<Ascii<S1>> for &'a str {
84    #[inline]
85    fn eq(&self, other: &Ascii<S1>) -> bool {
86        other == self
87    }
88}
89
90impl<S1: AsRef<str>, S2: AsRef<str>> PartialEq<S2> for Ascii<S1> {
91    #[inline]
92    fn eq(&self, other: &S2) -> bool {
93        self.as_ref().eq_ignore_ascii_case(other.as_ref())
94    }
95}
96
97impl<S: AsRef<str>> Eq for Ascii<S> {}
98
99impl<S: FromStr> FromStr for Ascii<S> {
100    type Err = <S as FromStr>::Err;
101    fn from_str(s: &str) -> Result<Ascii<S>, <S as FromStr>::Err> {
102        s.parse().map(Ascii)
103    }
104}
105
106impl<S: AsRef<str>> Hash for Ascii<S> {
107    #[inline]
108    fn hash<H: Hasher>(&self, hasher: &mut H) {
109        for byte in self.as_ref().bytes().map(|b| b.to_ascii_lowercase()) {
110            hasher.write_u8(byte);
111        }
112        // prefix-freedom
113        hasher.write_u8(0xFF);
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::Ascii;
120    use std::collections::hash_map::DefaultHasher;
121    use std::hash::{Hash, Hasher};
122    use std::string::String;
123
124    fn hash<T: Hash>(t: &T) -> u64 {
125        let mut s = DefaultHasher::new();
126        t.hash(&mut s);
127        s.finish()
128    }
129
130    #[test]
131    fn test_case_insensitive() {
132        let a = Ascii("foobar");
133        let b = Ascii("FOOBAR");
134
135        assert_eq!(a, b);
136        assert_eq!(hash(&a), hash(&b));
137
138        assert_eq!(a, "fooBar");
139        assert_eq!("fooBar", a);
140        assert_eq!(String::from("fooBar"), a);
141        assert_eq!(a, String::from("fooBar"));
142    }
143
144    #[cfg(feature = "nightly")]
145    #[bench]
146    fn bench_ascii_eq(b: &mut ::test::Bencher) {
147        b.bytes = b"foobar".len() as u64;
148        b.iter(|| assert_eq!(Ascii("foobar"), Ascii("FOOBAR")));
149    }
150
151    #[test]
152    fn test_case_cmp() {
153        assert!(Ascii("foobar") == Ascii("FOOBAR"));
154        assert!(Ascii("a") < Ascii("B"));
155
156        assert!(Ascii("A") < Ascii("b"));
157        assert!(Ascii("aa") > Ascii("a"));
158
159        assert!(Ascii("a") < Ascii("aa"));
160        assert!(Ascii("a") < Ascii("AA"));
161    }
162
163    #[test]
164    fn test_ascii_new_const() {
165        const _ASCII: Ascii<&'static str> = Ascii::new("");
166    }
167}