]> git.lizzy.rs Git - rust.git/blob - src/libstd/str/ascii.rs
dd730f2068973d67534586d69d03c4bacb99446e
[rust.git] / src / libstd / str / ascii.rs
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Operations on ASCII strings and characters.
12
13 use to_str::{ToStr,ToStrConsume};
14 use str;
15 use str::StrSlice;
16 use cast;
17 use iterator::{Iterator, IteratorUtil};
18 use vec::{CopyableVector, ImmutableVector, OwnedVector};
19 use to_bytes::IterBytes;
20 use option::{Some, None};
21
22 /// Datatype to hold one ascii character. It wraps a `u8`, with the highest bit always zero.
23 #[deriving(Clone, Eq)]
24 pub struct Ascii { priv chr: u8 }
25
26 impl Ascii {
27     /// Converts a ascii character into a `u8`.
28     #[inline]
29     pub fn to_byte(self) -> u8 {
30         self.chr
31     }
32
33     /// Converts a ascii character into a `char`.
34     #[inline]
35     pub fn to_char(self) -> char {
36         self.chr as char
37     }
38
39     /// Convert to lowercase.
40     #[inline]
41     pub fn to_lower(self) -> Ascii {
42         if self.chr >= 65 && self.chr <= 90 {
43             Ascii{chr: self.chr | 0x20 }
44         } else {
45             self
46         }
47     }
48
49     /// Convert to uppercase.
50     #[inline]
51     pub fn to_upper(self) -> Ascii {
52         if self.chr >= 97 && self.chr <= 122 {
53             Ascii{chr: self.chr & !0x20 }
54         } else {
55             self
56         }
57     }
58
59     /// Compares two ascii characters of equality, ignoring case.
60     #[inline]
61     pub fn eq_ignore_case(self, other: Ascii) -> bool {
62         self.to_lower().chr == other.to_lower().chr
63     }
64 }
65
66 impl ToStr for Ascii {
67     #[inline]
68     fn to_str(&self) -> ~str { str::from_bytes(['\'' as u8, self.chr, '\'' as u8]) }
69 }
70
71 /// Trait for converting into an ascii type.
72 pub trait AsciiCast<T> {
73     /// Convert to an ascii type
74     fn to_ascii(&self) -> T;
75
76     /// Convert to an ascii type, not doing any range asserts
77     unsafe fn to_ascii_nocheck(&self) -> T;
78
79     /// Check if convertible to ascii
80     fn is_ascii(&self) -> bool;
81 }
82
83 impl<'self> AsciiCast<&'self[Ascii]> for &'self [u8] {
84     #[inline]
85     fn to_ascii(&self) -> &'self[Ascii] {
86         assert!(self.is_ascii());
87         unsafe {self.to_ascii_nocheck()}
88     }
89
90     #[inline]
91     unsafe fn to_ascii_nocheck(&self) -> &'self[Ascii] {
92         cast::transmute(*self)
93     }
94
95     #[inline]
96     fn is_ascii(&self) -> bool {
97         for b in self.iter() {
98             if !b.is_ascii() { return false; }
99         }
100         true
101     }
102 }
103
104 impl<'self> AsciiCast<&'self[Ascii]> for &'self str {
105     #[inline]
106     fn to_ascii(&self) -> &'self[Ascii] {
107         assert!(self.is_ascii());
108         unsafe {self.to_ascii_nocheck()}
109     }
110
111     #[inline]
112     unsafe fn to_ascii_nocheck(&self) -> &'self[Ascii] {
113         let (p,len): (*u8, uint) = cast::transmute(*self);
114         cast::transmute((p, len - 1))
115     }
116
117     #[inline]
118     fn is_ascii(&self) -> bool {
119         self.byte_iter().all(|b| b.is_ascii())
120     }
121 }
122
123 impl AsciiCast<Ascii> for u8 {
124     #[inline]
125     fn to_ascii(&self) -> Ascii {
126         assert!(self.is_ascii());
127         unsafe {self.to_ascii_nocheck()}
128     }
129
130     #[inline]
131     unsafe fn to_ascii_nocheck(&self) -> Ascii {
132         Ascii{ chr: *self }
133     }
134
135     #[inline]
136     fn is_ascii(&self) -> bool {
137         *self & 128 == 0u8
138     }
139 }
140
141 impl AsciiCast<Ascii> for char {
142     #[inline]
143     fn to_ascii(&self) -> Ascii {
144         assert!(self.is_ascii());
145         unsafe {self.to_ascii_nocheck()}
146     }
147
148     #[inline]
149     unsafe fn to_ascii_nocheck(&self) -> Ascii {
150         Ascii{ chr: *self as u8 }
151     }
152
153     #[inline]
154     fn is_ascii(&self) -> bool {
155         *self - ('\x7F' & *self) == '\x00'
156     }
157 }
158
159 /// Trait for copyless casting to an ascii vector.
160 pub trait OwnedAsciiCast {
161     /// Take ownership and cast to an ascii vector without trailing zero element.
162     fn into_ascii(self) -> ~[Ascii];
163
164     /// Take ownership and cast to an ascii vector without trailing zero element.
165     /// Does not perform validation checks.
166     unsafe fn into_ascii_nocheck(self) -> ~[Ascii];
167 }
168
169 impl OwnedAsciiCast for ~[u8] {
170     #[inline]
171     fn into_ascii(self) -> ~[Ascii] {
172         assert!(self.is_ascii());
173         unsafe {self.into_ascii_nocheck()}
174     }
175
176     #[inline]
177     unsafe fn into_ascii_nocheck(self) -> ~[Ascii] {
178         cast::transmute(self)
179     }
180 }
181
182 impl OwnedAsciiCast for ~str {
183     #[inline]
184     fn into_ascii(self) -> ~[Ascii] {
185         assert!(self.is_ascii());
186         unsafe {self.into_ascii_nocheck()}
187     }
188
189     #[inline]
190     unsafe fn into_ascii_nocheck(self) -> ~[Ascii] {
191         let mut r: ~[Ascii] = cast::transmute(self);
192         r.pop();
193         r
194     }
195 }
196
197 /// Trait for converting an ascii type to a string. Needed to convert `&[Ascii]` to `~str`
198 pub trait AsciiStr {
199     /// Convert to a string.
200     fn to_str_ascii(&self) -> ~str;
201
202     /// Convert to vector representing a lower cased ascii string.
203     fn to_lower(&self) -> ~[Ascii];
204
205     /// Convert to vector representing a upper cased ascii string.
206     fn to_upper(&self) -> ~[Ascii];
207
208     /// Compares two Ascii strings ignoring case
209     fn eq_ignore_case(self, other: &[Ascii]) -> bool;
210 }
211
212 impl<'self> AsciiStr for &'self [Ascii] {
213     #[inline]
214     fn to_str_ascii(&self) -> ~str {
215         let mut cpy = self.to_owned();
216         cpy.push(0u8.to_ascii());
217         unsafe {cast::transmute(cpy)}
218     }
219
220     #[inline]
221     fn to_lower(&self) -> ~[Ascii] {
222         self.map(|a| a.to_lower())
223     }
224
225     #[inline]
226     fn to_upper(&self) -> ~[Ascii] {
227         self.map(|a| a.to_upper())
228     }
229
230     #[inline]
231     fn eq_ignore_case(self, other: &[Ascii]) -> bool {
232         do self.iter().zip(other.iter()).all |(&a, &b)| { a.eq_ignore_case(b) }
233     }
234 }
235
236 impl ToStrConsume for ~[Ascii] {
237     #[inline]
238     fn into_str(self) -> ~str {
239         let mut cpy = self;
240         cpy.push(0u8.to_ascii());
241         unsafe {cast::transmute(cpy)}
242     }
243 }
244
245 impl IterBytes for Ascii {
246     #[inline]
247     fn iter_bytes(&self, _lsb0: bool, f: &fn(buf: &[u8]) -> bool) -> bool {
248         f([self.to_byte()])
249     }
250 }
251
252 /// Trait to convert to a owned byte array by consuming self
253 pub trait ToBytesConsume {
254     /// Converts to a owned byte array by consuming self
255     fn into_bytes(self) -> ~[u8];
256 }
257
258 impl ToBytesConsume for ~[Ascii] {
259     fn into_bytes(self) -> ~[u8] {
260         unsafe {cast::transmute(self)}
261     }
262 }
263
264 #[cfg(test)]
265 mod tests {
266     use super::*;
267     use to_bytes::ToBytes;
268
269     macro_rules! v2ascii (
270         ( [$($e:expr),*]) => ( [$(Ascii{chr:$e}),*]);
271         (~[$($e:expr),*]) => (~[$(Ascii{chr:$e}),*]);
272     )
273
274     #[test]
275     fn test_ascii() {
276         assert_eq!(65u8.to_ascii().to_byte(), 65u8);
277         assert_eq!(65u8.to_ascii().to_char(), 'A');
278         assert_eq!('A'.to_ascii().to_char(), 'A');
279         assert_eq!('A'.to_ascii().to_byte(), 65u8);
280
281         assert_eq!('A'.to_ascii().to_lower().to_char(), 'a');
282         assert_eq!('Z'.to_ascii().to_lower().to_char(), 'z');
283         assert_eq!('a'.to_ascii().to_upper().to_char(), 'A');
284         assert_eq!('z'.to_ascii().to_upper().to_char(), 'Z');
285
286         assert_eq!('@'.to_ascii().to_lower().to_char(), '@');
287         assert_eq!('['.to_ascii().to_lower().to_char(), '[');
288         assert_eq!('`'.to_ascii().to_upper().to_char(), '`');
289         assert_eq!('{'.to_ascii().to_upper().to_char(), '{');
290
291         assert!("banana".iter().all(|c| c.is_ascii()));
292         assert!(!"ประเทศไทย中华Việt Nam".iter().all(|c| c.is_ascii()));
293     }
294
295     #[test]
296     fn test_ascii_vec() {
297         assert_eq!((&[40u8, 32u8, 59u8]).to_ascii(), v2ascii!([40, 32, 59]));
298         assert_eq!("( ;".to_ascii(),                 v2ascii!([40, 32, 59]));
299         // FIXME: #5475 borrowchk error, owned vectors do not live long enough
300         // if chained-from directly
301         let v = ~[40u8, 32u8, 59u8]; assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59]));
302         let v = ~"( ;";              assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59]));
303
304         assert_eq!("abCDef&?#".to_ascii().to_lower().to_str_ascii(), ~"abcdef&?#");
305         assert_eq!("abCDef&?#".to_ascii().to_upper().to_str_ascii(), ~"ABCDEF&?#");
306
307         assert_eq!("".to_ascii().to_lower().to_str_ascii(), ~"");
308         assert_eq!("YMCA".to_ascii().to_lower().to_str_ascii(), ~"ymca");
309         assert_eq!("abcDEFxyz:.;".to_ascii().to_upper().to_str_ascii(), ~"ABCDEFXYZ:.;");
310
311         assert!("aBcDeF&?#".to_ascii().eq_ignore_case("AbCdEf&?#".to_ascii()));
312
313         assert!("".is_ascii());
314         assert!("a".is_ascii());
315         assert!(!"\u2009".is_ascii());
316
317     }
318
319     #[test]
320     fn test_owned_ascii_vec() {
321         assert_eq!((~"( ;").into_ascii(), v2ascii!(~[40, 32, 59]));
322         assert_eq!((~[40u8, 32u8, 59u8]).into_ascii(), v2ascii!(~[40, 32, 59]));
323     }
324
325     #[test]
326     fn test_ascii_to_str() { assert_eq!(v2ascii!([40, 32, 59]).to_str_ascii(), ~"( ;"); }
327
328     #[test]
329     fn test_ascii_into_str() {
330         assert_eq!(v2ascii!(~[40, 32, 59]).into_str(), ~"( ;");
331     }
332
333     #[test]
334     fn test_ascii_to_bytes() {
335         assert_eq!(v2ascii!(~[40, 32, 59]).to_bytes(false), ~[40u8, 32u8, 59u8]);
336         assert_eq!(v2ascii!(~[40, 32, 59]).into_bytes(), ~[40u8, 32u8, 59u8]);
337     }
338
339     #[test] #[should_fail]
340     fn test_ascii_vec_fail_u8_slice()  { (&[127u8, 128u8, 255u8]).to_ascii(); }
341
342     #[test] #[should_fail]
343     fn test_ascii_vec_fail_str_slice() { "zoä华".to_ascii(); }
344
345     #[test] #[should_fail]
346     fn test_ascii_fail_u8_slice() { 255u8.to_ascii(); }
347
348     #[test] #[should_fail]
349     fn test_ascii_fail_char_slice() { 'λ'.to_ascii(); }
350 }