@@ -7,7 +7,7 @@ use notify::DebouncedEvent;
7
7
use notify:: { RecommendedWatcher , RecursiveMode , Watcher } ;
8
8
use std:: ffi:: OsStr ;
9
9
use std:: fs;
10
- use std:: io;
10
+ use std:: io:: { self , prelude :: * } ;
11
11
use std:: path:: Path ;
12
12
use std:: process:: { Command , Stdio } ;
13
13
use std:: sync:: mpsc:: channel;
@@ -58,6 +58,45 @@ fn main() {
58
58
SubCommand :: with_name ( "list" )
59
59
. alias ( "l" )
60
60
. about ( "Lists the exercises available in rustlings" )
61
+ . arg (
62
+ Arg :: with_name ( "paths" )
63
+ . long ( "paths" )
64
+ . short ( "p" )
65
+ . conflicts_with ( "names" )
66
+ . help ( "Show only the paths of the exercises" )
67
+ )
68
+ . arg (
69
+ Arg :: with_name ( "names" )
70
+ . long ( "names" )
71
+ . short ( "n" )
72
+ . conflicts_with ( "paths" )
73
+ . help ( "Show only the names of the exercises" )
74
+ )
75
+ . arg (
76
+ Arg :: with_name ( "filter" )
77
+ . long ( "filter" )
78
+ . short ( "f" )
79
+ . takes_value ( true )
80
+ . empty_values ( false )
81
+ . help (
82
+ "Provide a string to match the exercise names.\
83
+ \n Comma separated patterns are acceptable."
84
+ )
85
+ )
86
+ . arg (
87
+ Arg :: with_name ( "unsolved" )
88
+ . long ( "unsolved" )
89
+ . short ( "u" )
90
+ . conflicts_with ( "solved" )
91
+ . help ( "Display only exercises not yet solved" )
92
+ )
93
+ . arg (
94
+ Arg :: with_name ( "solved" )
95
+ . long ( "solved" )
96
+ . short ( "s" )
97
+ . conflicts_with ( "unsolved" )
98
+ . help ( "Display only exercises that have been solved" )
99
+ )
61
100
)
62
101
. get_matches ( ) ;
63
102
@@ -93,9 +132,51 @@ fn main() {
93
132
let exercises = toml:: from_str :: < ExerciseList > ( toml_str) . unwrap ( ) . exercises ;
94
133
let verbose = matches. is_present ( "nocapture" ) ;
95
134
96
- if matches. subcommand_matches ( "list" ) . is_some ( ) {
97
- exercises. iter ( ) . for_each ( |e| println ! ( "{}" , e. name) ) ;
135
+ // Handle the list command
136
+ if let Some ( list_m) = matches. subcommand_matches ( "list" ) {
137
+ if [ "paths" , "names" ] . iter ( ) . all ( |k| !list_m. is_present ( k) ) {
138
+ println ! ( "{:<17}\t {:<46}\t {:<7}" , "Name" , "Path" , "Status" ) ;
139
+ }
140
+ let filters = list_m. value_of ( "filter" ) . unwrap_or_default ( ) . to_lowercase ( ) ;
141
+ exercises. iter ( ) . for_each ( |e| {
142
+ let fname = format ! ( "{}" , e. path. display( ) ) ;
143
+ let filter_cond = filters
144
+ . split ( ',' )
145
+ . filter ( |f| f. trim ( ) . len ( ) > 0 )
146
+ . any ( |f| e. name . contains ( & f) || fname. contains ( & f) ) ;
147
+ let status = if e. looks_done ( ) { "Done" } else { "Pending" } ;
148
+ let solve_cond = {
149
+ ( e. looks_done ( ) && list_m. is_present ( "solved" ) )
150
+ || ( !e. looks_done ( ) && list_m. is_present ( "unsolved" ) )
151
+ || ( !list_m. is_present ( "solved" ) && !list_m. is_present ( "unsolved" ) )
152
+ } ;
153
+ if solve_cond && ( filter_cond || !list_m. is_present ( "filter" ) ) {
154
+ let line = if list_m. is_present ( "paths" ) {
155
+ format ! ( "{}\n " , fname)
156
+ } else if list_m. is_present ( "names" ) {
157
+ format ! ( "{}\n " , e. name)
158
+ } else {
159
+ format ! ( "{:<17}\t {:<46}\t {:<7}\n " , e. name, fname, status)
160
+ } ;
161
+ // Somehow using println! leads to the binary panicking
162
+ // when its output is piped.
163
+ // So, we're handling a Broken Pipe error and exiting with 0 anyway
164
+ let stdout = std:: io:: stdout ( ) ;
165
+ {
166
+ let mut handle = stdout. lock ( ) ;
167
+ handle. write_all ( line. as_bytes ( ) ) . unwrap_or_else ( |e| {
168
+ match e. kind ( ) {
169
+ std:: io:: ErrorKind :: BrokenPipe => std:: process:: exit ( 0 ) ,
170
+ _ => std:: process:: exit ( 1 ) ,
171
+ } ;
172
+ } ) ;
173
+ }
174
+ }
175
+ } ) ;
176
+ std:: process:: exit ( 0 ) ;
98
177
}
178
+
179
+ // Handle the run command
99
180
if let Some ( ref matches) = matches. subcommand_matches ( "run" ) {
100
181
let name = matches. value_of ( "name" ) . unwrap ( ) ;
101
182
@@ -123,13 +204,18 @@ fn main() {
123
204
println ! ( "{}" , exercise. hint) ;
124
205
}
125
206
207
+ // Handle the verify command
126
208
if matches. subcommand_matches ( "verify" ) . is_some ( ) {
127
209
verify ( & exercises, verbose) . unwrap_or_else ( |_| std:: process:: exit ( 1 ) ) ;
128
210
}
129
211
212
+ // Handle the watch command
130
213
if matches. subcommand_matches ( "watch" ) . is_some ( ) {
131
214
if let Err ( e) = watch ( & exercises, verbose) {
132
- println ! ( "Error: Could not watch your progess. Error message was {:?}." , e) ;
215
+ println ! (
216
+ "Error: Could not watch your progess. Error message was {:?}." ,
217
+ e
218
+ ) ;
133
219
println ! ( "Most likely you've run out of disk space or your 'inotify limit' has been reached." ) ;
134
220
std:: process:: exit ( 1 ) ;
135
221
}
@@ -138,24 +224,24 @@ fn main() {
138
224
emoji = Emoji ( "🎉" , "★" )
139
225
) ;
140
226
println ! ( ) ;
141
- println ! ( "+----------------------------------------------------+" ) ;
142
- println ! ( "| You made it to the Fe-nish line! |" ) ;
143
- println ! ( "+-------------------------- ------------------------+" ) ;
227
+ println ! ( "+----------------------------------------------------+" ) ;
228
+ println ! ( "| You made it to the Fe-nish line! |" ) ;
229
+ println ! ( "+-------------------------- ------------------------+" ) ;
144
230
println ! ( " \\ / " ) ;
145
- println ! ( " ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ " ) ;
146
- println ! ( " ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ " ) ;
147
- println ! ( " ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ " ) ;
148
- println ! ( " ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒ " ) ;
149
- println ! ( " ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓ " ) ;
150
- println ! ( " ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒ " ) ;
151
- println ! ( " ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒ " ) ;
152
- println ! ( " ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒ " ) ;
231
+ println ! ( " ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ " ) ;
232
+ println ! ( " ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ " ) ;
233
+ println ! ( " ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ " ) ;
234
+ println ! ( " ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒ " ) ;
235
+ println ! ( " ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓ " ) ;
236
+ println ! ( " ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒ " ) ;
237
+ println ! ( " ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒ " ) ;
238
+ println ! ( " ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒ " ) ;
153
239
println ! ( " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ " ) ;
154
240
println ! ( " ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒ " ) ;
155
- println ! ( " ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒ " ) ;
156
- println ! ( " ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ " ) ;
157
- println ! ( " ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒ " ) ;
158
- println ! ( " ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ " ) ;
241
+ println ! ( " ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒ " ) ;
242
+ println ! ( " ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ " ) ;
243
+ println ! ( " ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒ " ) ;
244
+ println ! ( " ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ " ) ;
159
245
println ! ( " ▒▒ ▒▒ ▒▒ ▒▒ " ) ;
160
246
println ! ( ) ;
161
247
println ! ( "We hope you enjoyed learning about the various aspects of Rust!" ) ;
@@ -223,7 +309,13 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
223
309
let filepath = b. as_path ( ) . canonicalize ( ) . unwrap ( ) ;
224
310
let pending_exercises = exercises
225
311
. iter ( )
226
- . skip_while ( |e| !filepath. ends_with ( & e. path ) ) ;
312
+ . skip_while ( |e| !filepath. ends_with ( & e. path ) )
313
+ // .filter(|e| filepath.ends_with(&e.path))
314
+ . chain (
315
+ exercises
316
+ . iter ( )
317
+ . filter ( |e| !e. looks_done ( ) && !filepath. ends_with ( & e. path ) )
318
+ ) ;
227
319
clear_screen ( ) ;
228
320
match verify ( pending_exercises, verbose) {
229
321
Ok ( _) => return Ok ( ( ) ) ,
0 commit comments