1
+ use std:: str:: FromStr ;
2
+
1
3
/// A `Scanner` is a simple utility for parsing strings, allowing access to words,
2
4
/// numbers, and lines from an input string.
3
5
///
@@ -19,47 +21,103 @@ impl<'a> Scanner<'a> {
19
21
/// # Examples
20
22
///
21
23
/// ```
24
+ /// use scanner::scanner::Scanner;
22
25
/// let scanner = Scanner::new("Hello, world!");
23
26
/// ```
24
27
pub fn new ( input : & ' a str ) -> Self {
25
28
Scanner { input, position : 0 }
26
29
}
27
30
28
- /// Scans for the next number in the input string.
31
+ /// Scans for the next token in the input string based on a provided predicate .
29
32
///
30
- /// Parses a contiguous sequence of digits, including an optional leading
31
- /// minus sign for negative numbers. Consumes the number from the input
32
- /// and updates the scanner's position.
33
+ /// A token is defined as a contiguous sequence of characters that satisfy the
34
+ /// condition defined by the `predicate` closure. This method will consume the
35
+ /// characters matching the predicate from the input string and update the scanner's position.
36
+ ///
37
+ /// # Arguments
38
+ ///
39
+ /// * `predicate`: A closure that takes a character as input and returns a boolean,
40
+ /// determining whether the character should be considered part of the token.
33
41
///
34
42
/// # Returns
35
43
///
36
- /// * `Some(i32) ` if a valid number is found.
37
- /// * `None` if no valid number is found.
44
+ /// * `Option<&'a str> ` if a valid token is found.
45
+ /// * `None` if no valid token can be found.
38
46
///
39
47
/// # Examples
40
48
///
41
49
/// ```
42
- /// let mut scanner = Scanner::new("42 is the answer");
43
- /// assert_eq!(scanner.next_number(), Some(42));
50
+ /// use scanner::scanner::Scanner;
51
+ /// let mut scanner = Scanner::new("Hello, world!");
52
+ /// // Scan for words (non-whitespace characters)
53
+ /// assert_eq!(scanner.next_token(|c, _| !c.is_whitespace()), Some("Hello,"));
54
+ ///
55
+ /// let mut scanner = Scanner::new("123 456 789");
56
+ /// // Scan for numbers (digits)
57
+ /// assert_eq!(scanner.next_token(|c, _| c.is_digit(10)), Some("123"));
44
58
/// ```
45
- pub fn next_number ( & mut self ) -> Option < i32 > {
59
+ pub fn next_token < F > ( & mut self , predicate : F ) -> Option < & ' a str >
60
+ where
61
+ F : Fn ( char , usize ) -> bool ,
62
+ {
46
63
let remaining = self . get_remaining ( ) ;
47
64
48
- let ( number_len, valid_chars_count) : ( usize , usize ) = remaining
49
- . trim_start ( )
50
- . char_indices ( )
51
- . take_while ( |& ( _, c) | c. is_digit ( 10 ) || c == '-' )
52
- . fold ( ( 0 , 0 ) , |( _, count) , ( i, _) | ( i + 1 , count + 1 ) ) ;
53
- let number_len = number_len + remaining. len ( ) - remaining. trim_start ( ) . len ( ) ;
65
+ let mut token_len: usize = 0 ;
66
+ let mut valid_chars_count: usize = 0 ;
67
+
68
+ for ( i, c) in remaining. char_indices ( ) {
69
+ if predicate ( c, i) {
70
+ valid_chars_count += 1 ;
71
+ token_len = i + 1 ;
72
+ } else {
73
+ if valid_chars_count > 0 {
74
+ break ;
75
+ }
76
+ token_len = i; // update token length to exclude this character
77
+ }
78
+ }
54
79
55
80
if valid_chars_count > 0 {
56
- self . position += number_len ;
57
- Some ( remaining[ ..number_len ] . trim_start ( ) . parse :: < i32 > ( ) . ok ( ) ? )
81
+ self . position += token_len ;
82
+ Some ( remaining[ ..token_len ] . trim_start ( ) )
58
83
} else {
59
84
None
60
85
}
61
86
}
62
87
88
+ /// Scans for the next number in the input string.
89
+ ///
90
+ /// Parses a contiguous sequence of digits, including an optional leading
91
+ /// minus sign for negative numbers. Consumes the number from the input
92
+ /// and updates the scanner's position.
93
+ ///
94
+ /// # Returns
95
+ ///
96
+ /// * `Some(T)` if a valid number is found.
97
+ /// * `None` if no valid number is found.
98
+ ///
99
+ /// # Examples
100
+ ///
101
+ /// ```
102
+ /// use scanner::scanner::Scanner;
103
+ /// let mut scanner = Scanner::new("42 is the answer");
104
+ /// assert_eq!(scanner.next_number(), Some(42));
105
+ /// ```
106
+ pub fn next_number < T > ( & mut self ) -> Option < T >
107
+ where
108
+ T : FromStr ,
109
+ {
110
+ let position = self . position ;
111
+ self . next_token ( |c, i| c. is_digit ( 10 ) || ( c == '-' && i == 0 ) )
112
+ . and_then ( |token| match token. parse :: < T > ( ) {
113
+ Ok ( number) => Some ( number) ,
114
+ Err ( _) => {
115
+ self . position = position;
116
+ None
117
+ }
118
+ } )
119
+ }
120
+
63
121
/// Scans for the next word in the input string.
64
122
///
65
123
/// A word is defined as a contiguous sequence of non-whitespace characters.
@@ -73,34 +131,12 @@ impl<'a> Scanner<'a> {
73
131
/// # Examples
74
132
///
75
133
/// ```
134
+ /// use scanner::scanner::Scanner;
76
135
/// let mut scanner = Scanner::new("Hello, world!");
77
136
/// assert_eq!(scanner.next_word(), Some("Hello,"));
78
137
/// ```
79
138
pub fn next_word ( & mut self ) -> Option < & ' a str > {
80
- let remaining = self . get_remaining ( ) ;
81
-
82
- let mut word_len: usize = 0 ;
83
- let mut valid_chars_count: usize = 0 ;
84
-
85
- for ( i, c) in remaining. char_indices ( ) {
86
- if !c. is_whitespace ( ) {
87
- valid_chars_count += 1 ;
88
- word_len = i + 1 ;
89
- } else {
90
- word_len = i;
91
-
92
- if valid_chars_count > 0 {
93
- break ;
94
- }
95
- }
96
- }
97
-
98
- if valid_chars_count > 0 {
99
- self . position += word_len;
100
- Some ( remaining[ ..word_len] . trim_start ( ) )
101
- } else {
102
- None
103
- }
139
+ self . next_token ( |c, _| !c. is_whitespace ( ) )
104
140
}
105
141
106
142
/// Scans for the next line from the input string.
@@ -117,6 +153,7 @@ impl<'a> Scanner<'a> {
117
153
/// # Examples
118
154
///
119
155
/// ```
156
+ /// use scanner::scanner::Scanner;
120
157
/// let mut scanner = Scanner::new("First line\nSecond line");
121
158
/// assert_eq!(scanner.next_line(), Some("First line"));
122
159
/// ```
@@ -146,9 +183,10 @@ impl<'a> Scanner<'a> {
146
183
/// # Examples
147
184
///
148
185
/// ```
186
+ /// use scanner::scanner::Scanner;
149
187
/// let mut scanner = Scanner::new("Hello world!");
150
188
/// scanner.next_word();
151
- /// assert_eq!(scanner.get_remaining(), "world!");
189
+ /// assert_eq!(scanner.get_remaining(), " world!");
152
190
/// ```
153
191
pub fn get_remaining ( & self ) -> & ' a str {
154
192
& self . input [ self . position ..]
@@ -162,7 +200,7 @@ mod tests {
162
200
#[ test]
163
201
fn test_empty ( ) {
164
202
let mut scanner = Scanner :: new ( "" ) ;
165
- assert_eq ! ( scanner. next_number( ) , None ) ;
203
+ assert_eq ! ( scanner. next_number:: < i32 > ( ) , None ) ;
166
204
}
167
205
168
206
#[ test]
@@ -199,13 +237,13 @@ mod tests {
199
237
assert_eq ! ( scanner. next_number( ) , Some ( -30 ) ) ;
200
238
assert_eq ! ( scanner. next_number( ) , Some ( 33 ) ) ;
201
239
assert_eq ! ( scanner. next_number( ) , Some ( 85 ) ) ;
202
- assert_eq ! ( scanner. next_number( ) , None ) ;
240
+ assert_eq ! ( scanner. next_number:: < i32 > ( ) , None ) ;
203
241
}
204
242
205
243
#[ test]
206
244
fn test_next_number_no_digits ( ) {
207
245
let mut scanner = Scanner :: new ( "no numbers here" ) ;
208
- assert_eq ! ( scanner. next_number( ) , None ) ;
246
+ assert_eq ! ( scanner. next_number:: < i32 > ( ) , None ) ;
209
247
assert_eq ! ( scanner. get_remaining( ) , "no numbers here" ) ;
210
248
}
211
249
@@ -215,56 +253,56 @@ mod tests {
215
253
assert_eq ! ( scanner. next_number( ) , Some ( 55 ) ) ;
216
254
assert_eq ! ( scanner. get_remaining( ) , " 88" ) ;
217
255
assert_eq ! ( scanner. next_number( ) , Some ( 88 ) ) ;
218
- assert_eq ! ( scanner. next_number( ) , None ) ;
256
+ assert_eq ! ( scanner. next_number:: < i32 > ( ) , None ) ;
219
257
}
220
258
221
259
#[ test]
222
260
fn test_get_remaining ( ) {
223
261
let mut scanner = Scanner :: new ( "123 456" ) ;
224
262
assert_eq ! ( scanner. get_remaining( ) , "123 456" ) ;
225
- scanner. next_number ( ) ;
263
+ scanner. next_number :: < i32 > ( ) ;
226
264
assert_eq ! ( scanner. get_remaining( ) , " 456" ) ;
227
- scanner. next_number ( ) ;
265
+ scanner. next_number :: < i32 > ( ) ;
228
266
assert_eq ! ( scanner. get_remaining( ) , "" ) ;
229
267
}
230
268
231
269
#[ test]
232
270
fn test_next_number_non_digit_characters ( ) {
233
271
let mut scanner = Scanner :: new ( "abc 123 xyz 456" ) ;
234
272
235
- assert_eq ! ( scanner. next_number( ) , None ) ;
273
+ assert_eq ! ( scanner. next_number:: < i32 > ( ) , None ) ;
236
274
assert_eq ! ( scanner. get_remaining( ) , "abc 123 xyz 456" ) ;
237
275
assert_eq ! ( scanner. next_word( ) , Some ( "abc" ) ) ;
238
276
assert_eq ! ( scanner. get_remaining( ) , " 123 xyz 456" ) ;
239
277
assert_eq ! ( scanner. next_word( ) , Some ( "123" ) ) ;
240
278
assert_eq ! ( scanner. get_remaining( ) , " xyz 456" ) ;
241
- assert_eq ! ( scanner. next_number( ) , None ) ;
279
+ assert_eq ! ( scanner. next_number:: < i32 > ( ) , None ) ;
242
280
assert_eq ! ( scanner. get_remaining( ) , " xyz 456" ) ;
243
281
assert_eq ! ( scanner. next_word( ) , Some ( "xyz" ) ) ;
244
282
assert_eq ! ( scanner. get_remaining( ) , " 456" ) ;
245
283
assert_eq ! ( scanner. next_word( ) , Some ( "456" ) ) ;
246
284
assert_eq ! ( scanner. get_remaining( ) , "" ) ;
247
285
}
248
286
249
- // #[test]
250
- // fn test_negative_sign_between_numbers() {
251
- // let mut scanner = Scanner::new("5-3");
252
- //
253
- // assert_eq!(scanner.next_number(), Some(5));
254
- // assert_eq!(scanner.get_remaining(), "-3");
255
- // assert_eq!(scanner.next_number(), Some(-3));
256
- //
257
- // let mut scanner = Scanner::new("5 - 3");
258
- //
259
- // assert_eq!(scanner.next_number(), Some(5));
260
- // assert_eq!(scanner.get_remaining(), " - 3");
261
- // assert_eq!(scanner.next_number(), None);
262
- // assert_eq!(scanner.get_remaining(), " - 3");
263
- // assert_eq!(scanner.next_word(), Some("-"));
264
- // assert_eq!(scanner.get_remaining(), " 3");
265
- // assert_eq!(scanner.next_number(), Some(3));
266
- // assert_eq!(scanner.get_remaining(), "");
267
- // }
287
+ #[ test]
288
+ fn test_negative_sign_between_numbers ( ) {
289
+ let mut scanner = Scanner :: new ( "5-3" ) ;
290
+
291
+ assert_eq ! ( scanner. next_number( ) , Some ( 5 ) ) ;
292
+ assert_eq ! ( scanner. get_remaining( ) , "-3" ) ;
293
+ assert_eq ! ( scanner. next_number( ) , Some ( -3 ) ) ;
294
+
295
+ let mut scanner = Scanner :: new ( "5 - 3" ) ;
296
+
297
+ assert_eq ! ( scanner. next_number( ) , Some ( 5 ) ) ;
298
+ assert_eq ! ( scanner. get_remaining( ) , " - 3" ) ;
299
+ assert_eq ! ( scanner. next_number:: < i32 > ( ) , None ) ;
300
+ assert_eq ! ( scanner. get_remaining( ) , " - 3" ) ;
301
+ assert_eq ! ( scanner. next_word( ) , Some ( "-" ) ) ;
302
+ assert_eq ! ( scanner. get_remaining( ) , " 3" ) ;
303
+ assert_eq ! ( scanner. next_number( ) , Some ( 3 ) ) ;
304
+ assert_eq ! ( scanner. get_remaining( ) , "" ) ;
305
+ }
268
306
269
307
#[ test]
270
308
fn test_next_line ( ) {
0 commit comments