@@ -2,6 +2,7 @@ extern crate core;
2
2
3
3
use self :: core:: { fmt, mem} ;
4
4
use self :: core:: ops:: { Index , IndexMut , Range , RangeTo , RangeFrom , RangeFull } ;
5
+ use self :: core:: slice:: { Iter , IterMut } ;
5
6
#[ cfg( feature = "std" ) ]
6
7
use std:: error:: Error ;
7
8
#[ cfg( feature = "std" ) ]
@@ -139,6 +140,32 @@ impl AsciiStr {
139
140
self . len ( ) == 0
140
141
}
141
142
143
+ /// Returns an iterator over the characters of the `AsciiStr`.
144
+ #[ inline]
145
+ pub fn chars ( & self ) -> Chars {
146
+ self . slice . iter ( )
147
+ }
148
+
149
+ /// Returns an iterator over the characters of the `AsciiStr` which allows you to modify the
150
+ /// value of each `AsciiChar`.
151
+ #[ inline]
152
+ pub fn chars_mut ( & mut self ) -> CharsMut {
153
+ self . slice . iter_mut ( )
154
+ }
155
+
156
+ /// Returns an iterator over the lines of the `AsciiStr`, which are themselves `AsciiStr`s.
157
+ ///
158
+ /// Lines are ended with either `LineFeed` (`\n`), or `CarriageReturn` then `LineFeed` (`\r\n`).
159
+ ///
160
+ /// The final line ending is optional.
161
+ #[ inline]
162
+ pub fn lines ( & self ) -> Lines {
163
+ Lines {
164
+ current_index : 0 ,
165
+ string : self
166
+ }
167
+ }
168
+
142
169
/// Returns an ASCII string slice with leading and trailing whitespace removed.
143
170
///
144
171
/// # Examples
@@ -388,6 +415,82 @@ impl AsciiExt for AsciiStr {
388
415
}
389
416
}
390
417
418
+ impl < ' a > IntoIterator for & ' a AsciiStr {
419
+ type Item = & ' a AsciiChar ;
420
+ type IntoIter = Chars < ' a > ;
421
+ #[ inline]
422
+ fn into_iter ( self ) -> Self :: IntoIter {
423
+ self . chars ( )
424
+ }
425
+ }
426
+
427
+ impl < ' a > IntoIterator for & ' a mut AsciiStr {
428
+ type Item = & ' a mut AsciiChar ;
429
+ type IntoIter = CharsMut < ' a > ;
430
+ #[ inline]
431
+ fn into_iter ( self ) -> Self :: IntoIter {
432
+ self . chars_mut ( )
433
+ }
434
+ }
435
+
436
+ /// An immutable iterator over the characters of an `AsciiStr`.
437
+ pub type Chars < ' a > = Iter < ' a , AsciiChar > ;
438
+
439
+ /// A mutable iterator over the characters of an `AsciiStr`.
440
+ pub type CharsMut < ' a > = IterMut < ' a , AsciiChar > ;
441
+
442
+ /// An iterator over the lines of the internal character array.
443
+ #[ derive( Clone , Debug ) ]
444
+ pub struct Lines < ' a > {
445
+ current_index : usize ,
446
+ string : & ' a AsciiStr
447
+ }
448
+
449
+ impl < ' a > Iterator for Lines < ' a > {
450
+ type Item = & ' a AsciiStr ;
451
+
452
+ fn next ( & mut self ) -> Option < Self :: Item > {
453
+ let curr_idx = self . current_index ;
454
+ let len = self . string . len ( ) ;
455
+ if curr_idx >= len {
456
+ return None ;
457
+ }
458
+
459
+ let mut next_idx = None ;
460
+ let mut linebreak_skip = 0 ;
461
+
462
+ for i in curr_idx..( len-1 ) {
463
+ match ( self . string [ i] , self . string [ i + 1 ] ) {
464
+ ( AsciiChar :: CarriageReturn , AsciiChar :: LineFeed ) => {
465
+ next_idx = Some ( i) ;
466
+ linebreak_skip = 2 ;
467
+ break ;
468
+ }
469
+ ( AsciiChar :: LineFeed , _) => {
470
+ next_idx = Some ( i) ;
471
+ linebreak_skip = 1 ;
472
+ break ;
473
+ }
474
+ _ => { }
475
+ }
476
+ }
477
+
478
+ let next_idx = match next_idx {
479
+ Some ( i) => i,
480
+ None => return None
481
+ } ;
482
+ let line = & self . string [ curr_idx..next_idx] ;
483
+
484
+ self . current_index = next_idx + linebreak_skip;
485
+
486
+ if line. is_empty ( ) && self . current_index == self . string . len ( ) {
487
+ // This is a trailing line break
488
+ None
489
+ } else {
490
+ Some ( line)
491
+ }
492
+ }
493
+ }
391
494
392
495
/// Error that is returned when a sequence of `u8` are not all ASCII.
393
496
///
@@ -604,6 +707,51 @@ mod tests {
604
707
assert_eq ! ( b, "A@A" ) ;
605
708
}
606
709
710
+ #[ test]
711
+ fn chars_iter ( ) {
712
+ let chars = & [ b'h' , b'e' , b'l' , b'l' , b'o' , b' ' , b'w' , b'o' , b'r' , b'l' , b'd' , b'\0' ] ;
713
+ let ascii = AsciiStr :: from_ascii ( chars) . unwrap ( ) ;
714
+ for ( achar, byte) in ascii. chars ( ) . zip ( chars. iter ( ) ) {
715
+ assert_eq ! ( achar, byte) ;
716
+ }
717
+ }
718
+
719
+ #[ test]
720
+ fn chars_iter_mut ( ) {
721
+ let mut chars = & mut [ b'h' , b'e' , b'l' , b'l' , b'o' , b' ' , b'w' , b'o' , b'r' , b'l' , b'd' , b'\0' ] ;
722
+ let mut ascii = chars. as_mut_ascii_str ( ) . unwrap ( ) ;
723
+
724
+ * ascii. chars_mut ( ) . next ( ) . unwrap ( ) = AsciiChar :: H ;
725
+
726
+ assert_eq ! ( ascii[ 0 ] , b'H' ) ;
727
+ }
728
+
729
+ #[ test]
730
+ fn lines_iter ( ) {
731
+ use super :: core:: iter:: Iterator ;
732
+ let lines: [ & str ; 3 ] = [ "great work" , "cool beans" , "awesome stuff" ] ;
733
+ let joined = "great work\n cool beans\r \n awesome stuff\n " ;
734
+ let ascii = AsciiStr :: from_ascii ( joined. as_bytes ( ) ) . unwrap ( ) ;
735
+ for ( asciiline, line) in ascii. lines ( ) . zip ( & lines) {
736
+ assert_eq ! ( asciiline, * line) ;
737
+ }
738
+
739
+ let trailing_line_break = b"\n " ;
740
+ let ascii = AsciiStr :: from_ascii ( & trailing_line_break) . unwrap ( ) ;
741
+ for _ in ascii. lines ( ) {
742
+ unreachable ! ( ) ;
743
+ }
744
+
745
+ let empty_lines = b"\n \r \n \n \r \n " ;
746
+ let mut ensure_iterated = false ;
747
+ let ascii = AsciiStr :: from_ascii ( & empty_lines) . unwrap ( ) ;
748
+ for line in ascii. lines ( ) {
749
+ ensure_iterated = true ;
750
+ assert ! ( line. is_empty( ) ) ;
751
+ }
752
+ assert ! ( ensure_iterated) ;
753
+ }
754
+
607
755
#[ test]
608
756
#[ cfg( feature = "std" ) ]
609
757
fn fmt_ascii_str ( ) {
0 commit comments