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 #[inline]
15 pub const fn new(s: S) -> Ascii<S> {
16 Ascii(s)
17 }
18
19 pub const fn into_unicase(self) -> UniCase<S> {
21 UniCase(Encoding::Ascii(self))
22 }
23
24 #[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 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}