1
- #![ allow( dead_code) ]
2
-
3
1
use std:: env;
4
2
use std:: error;
5
3
use std:: fmt;
6
4
use std:: fs;
7
- use std:: io;
5
+ use std:: io:: { self , BufRead } ;
8
6
use std:: str;
9
7
10
8
#[ cfg( test) ]
11
9
mod tests;
12
10
13
- /// States for parsing text
14
- #[ derive( Debug , Copy , Clone , Eq , PartialEq ) ]
15
- enum State {
16
- Normal , // within normal text
17
- Cr , // just saw \r
18
- Lf , // just saw \n
19
- }
20
-
21
11
struct FileArgs {
22
12
path : String ,
23
13
input : Vec < u8 > ,
24
- offset : usize ,
25
14
}
26
15
27
16
impl FileArgs {
28
- pub fn new ( path : String , input : Vec < u8 > ) -> Self {
29
- FileArgs { path, input, offset : 0 }
17
+ fn new ( path : String , input : Vec < u8 > ) -> Self {
18
+ FileArgs { path, input }
30
19
}
31
- }
32
-
33
- impl Iterator for FileArgs {
34
- type Item = Result < String , Error > ;
35
-
36
- fn next ( & mut self ) -> Option < Self :: Item > {
37
- if self . offset >= self . input . len ( ) {
38
- // All done
39
- return None ;
40
- }
41
-
42
- use State :: * ;
43
- let mut state = Normal ;
44
- let start = self . offset ;
45
- let mut end = start;
46
-
47
- for ( idx, b) in self . input [ start..] . iter ( ) . enumerate ( ) {
48
- let idx = start + idx + 1 ;
49
-
50
- self . offset = idx;
51
-
52
- match ( b, state) {
53
- ( b'\r' , Normal ) => state = Cr ,
54
- ( b'\n' , Normal ) => state = Lf ,
55
-
56
- ( b'\r' , Lf ) | ( b'\n' , Cr ) => {
57
- // Two-character line break (accept \r\n and \n\r(?)), so consume them both
58
- break ;
59
- }
60
-
61
- ( _, Cr ) | ( _, Lf ) => {
62
- // Peeked at character after single-character line break, so rewind to visit it
63
- // next time around.
64
- self . offset = idx - 1 ;
65
- break ;
66
- }
67
-
68
- ( _, _) => {
69
- end = idx;
70
- state = Normal ;
71
- }
72
- }
73
- }
74
20
75
- Some (
76
- String :: from_utf8 ( self . input [ start..end] . to_vec ( ) )
77
- . map_err ( |_| Error :: Utf8Error ( Some ( self . path . clone ( ) ) ) ) ,
78
- )
21
+ fn lines ( self ) -> impl Iterator < Item = Result < String , Error > > {
22
+ let Self { input, path } = self ;
23
+ io:: Cursor :: new ( input) . lines ( ) . map ( move |res| {
24
+ let path = path. clone ( ) ;
25
+ res. map_err ( move |err| match err. kind ( ) {
26
+ io:: ErrorKind :: InvalidData => Error :: Utf8Error ( Some ( path) ) ,
27
+ _ => Error :: IOError ( path, err) ,
28
+ } )
29
+ } )
79
30
}
80
31
}
81
32
82
33
pub struct ArgsIter {
83
34
base : env:: ArgsOs ,
84
- file : Option < FileArgs > ,
35
+ file : Option < Box < dyn Iterator < Item = Result < String , Error > > > > ,
85
36
}
86
37
87
38
impl ArgsIter {
@@ -107,13 +58,12 @@ impl Iterator for ArgsIter {
107
58
match arg {
108
59
Some ( Err ( err) ) => return Some ( Err ( err) ) ,
109
60
Some ( Ok ( ref arg) ) if arg. starts_with ( "@" ) => {
110
- // can't not be utf-8 now
111
- let path = str:: from_utf8 ( & arg. as_bytes ( ) [ 1 ..] ) . unwrap ( ) ;
112
- let file = match fs:: read ( path) {
113
- Ok ( file) => file,
61
+ let path = & arg[ 1 ..] ;
62
+ let lines = match fs:: read ( path) {
63
+ Ok ( file) => FileArgs :: new ( path. to_string ( ) , file) . lines ( ) ,
114
64
Err ( err) => return Some ( Err ( Error :: IOError ( path. to_string ( ) , err) ) ) ,
115
65
} ;
116
- self . file = Some ( FileArgs :: new ( path . to_string ( ) , file ) ) ;
66
+ self . file = Some ( Box :: new ( lines ) ) ;
117
67
}
118
68
Some ( Ok ( arg) ) => return Some ( Ok ( arg) ) ,
119
69
None => return None ,
0 commit comments