@@ -7,7 +7,7 @@ use notify::DebouncedEvent;
77use notify:: { RecommendedWatcher , RecursiveMode , Watcher } ;
88use std:: ffi:: OsStr ;
99use std:: fs;
10- use std:: io;
10+ use std:: io:: { self , prelude :: * } ;
1111use std:: path:: Path ;
1212use std:: process:: { Command , Stdio } ;
1313use std:: sync:: mpsc:: channel;
@@ -58,6 +58,45 @@ fn main() {
5858 SubCommand :: with_name ( "list" )
5959 . alias ( "l" )
6060 . 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+ )
61100 )
62101 . get_matches ( ) ;
63102
@@ -93,9 +132,51 @@ fn main() {
93132 let exercises = toml:: from_str :: < ExerciseList > ( toml_str) . unwrap ( ) . exercises ;
94133 let verbose = matches. is_present ( "nocapture" ) ;
95134
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 ) ;
98177 }
178+
179+ // Handle the run command
99180 if let Some ( ref matches) = matches. subcommand_matches ( "run" ) {
100181 let name = matches. value_of ( "name" ) . unwrap ( ) ;
101182
@@ -123,13 +204,18 @@ fn main() {
123204 println ! ( "{}" , exercise. hint) ;
124205 }
125206
207+ // Handle the verify command
126208 if matches. subcommand_matches ( "verify" ) . is_some ( ) {
127209 verify ( & exercises, verbose) . unwrap_or_else ( |_| std:: process:: exit ( 1 ) ) ;
128210 }
129211
212+ // Handle the watch command
130213 if matches. subcommand_matches ( "watch" ) . is_some ( ) {
131214 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+ ) ;
133219 println ! ( "Most likely you've run out of disk space or your 'inotify limit' has been reached." ) ;
134220 std:: process:: exit ( 1 ) ;
135221 }
@@ -138,24 +224,24 @@ fn main() {
138224 emoji = Emoji ( "🎉" , "★" )
139225 ) ;
140226 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 ! ( "+-------------------------- ------------------------+" ) ;
144230 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 ! ( " ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒ " ) ;
153239 println ! ( " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ " ) ;
154240 println ! ( " ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒ " ) ;
155- println ! ( " ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒ " ) ;
156- println ! ( " ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ " ) ;
157- println ! ( " ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒ " ) ;
158- println ! ( " ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ " ) ;
241+ println ! ( " ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒ " ) ;
242+ println ! ( " ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ " ) ;
243+ println ! ( " ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒ " ) ;
244+ println ! ( " ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ " ) ;
159245 println ! ( " ▒▒ ▒▒ ▒▒ ▒▒ " ) ;
160246 println ! ( ) ;
161247 println ! ( "We hope you enjoyed learning about the various aspects of Rust!" ) ;
@@ -223,7 +309,13 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
223309 let filepath = b. as_path ( ) . canonicalize ( ) . unwrap ( ) ;
224310 let pending_exercises = exercises
225311 . 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+ ) ;
227319 clear_screen ( ) ;
228320 match verify ( pending_exercises, verbose) {
229321 Ok ( _) => return Ok ( ( ) ) ,
0 commit comments