@@ -10,7 +10,8 @@ use std::fs;
1010use std:: io:: { self , prelude:: * } ;
1111use std:: path:: Path ;
1212use std:: process:: { Command , Stdio } ;
13- use std:: sync:: mpsc:: channel;
13+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
14+ use std:: sync:: mpsc:: { RecvTimeoutError , channel} ;
1415use std:: sync:: { Arc , Mutex } ;
1516use std:: thread;
1617use std:: time:: Duration ;
@@ -217,68 +218,76 @@ fn main() {
217218 }
218219
219220 Subcommands :: Watch ( _subargs) => {
220- if let Err ( e) = watch ( & exercises, verbose) {
221- println ! (
222- "Error: Could not watch your progress. Error message was {:?}." ,
223- e
224- ) ;
225- println ! ( "Most likely you've run out of disk space or your 'inotify limit' has been reached." ) ;
226- std:: process:: exit ( 1 ) ;
221+ match watch ( & exercises, verbose) {
222+ Err ( e) => {
223+ println ! (
224+ "Error: Could not watch your progress. Error message was {:?}." ,
225+ e
226+ ) ;
227+ println ! ( "Most likely you've run out of disk space or your 'inotify limit' has been reached." ) ;
228+ std:: process:: exit ( 1 ) ;
229+ }
230+ Ok ( WatchStatus :: Finished ) => {
231+ println ! (
232+ "{emoji} All exercises completed! {emoji}" ,
233+ emoji = Emoji ( "🎉" , "★" )
234+ ) ;
235+ println ! ( ) ;
236+ println ! ( "+----------------------------------------------------+" ) ;
237+ println ! ( "| You made it to the Fe-nish line! |" ) ;
238+ println ! ( "+-------------------------- ------------------------+" ) ;
239+ println ! ( " \\ / " ) ;
240+ println ! ( " ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ " ) ;
241+ println ! ( " ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ " ) ;
242+ println ! ( " ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ " ) ;
243+ println ! ( " ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒ " ) ;
244+ println ! ( " ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓ " ) ;
245+ println ! ( " ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒ " ) ;
246+ println ! ( " ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒ " ) ;
247+ println ! ( " ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒ " ) ;
248+ println ! ( " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ " ) ;
249+ println ! ( " ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒ " ) ;
250+ println ! ( " ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒ " ) ;
251+ println ! ( " ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ " ) ;
252+ println ! ( " ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒ " ) ;
253+ println ! ( " ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ " ) ;
254+ println ! ( " ▒▒ ▒▒ ▒▒ ▒▒ " ) ;
255+ println ! ( ) ;
256+ println ! ( "We hope you enjoyed learning about the various aspects of Rust!" ) ;
257+ println ! (
258+ "If you noticed any issues, please don't hesitate to report them to our repo."
259+ ) ;
260+ println ! ( "You can also contribute your own exercises to help the greater community!" ) ;
261+ println ! ( ) ;
262+ println ! ( "Before reporting an issue or contributing, please read our guidelines:" ) ;
263+ println ! ( "https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md" ) ;
264+ }
265+ Ok ( WatchStatus :: Unfinished ) => {
266+ println ! ( "We hope you're enjoying learning about Rust!" ) ;
267+ println ! ( "If you want to continue working on the exercises at a later point, you can simply run `rustlings watch` again" ) ;
268+ }
227269 }
228- println ! (
229- "{emoji} All exercises completed! {emoji}" ,
230- emoji = Emoji ( "🎉" , "★" )
231- ) ;
232- println ! ( ) ;
233- println ! ( "+----------------------------------------------------+" ) ;
234- println ! ( "| You made it to the Fe-nish line! |" ) ;
235- println ! ( "+-------------------------- ------------------------+" ) ;
236- println ! ( " \\ / " ) ;
237- println ! ( " ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ " ) ;
238- println ! ( " ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ " ) ;
239- println ! ( " ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ " ) ;
240- println ! ( " ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒ " ) ;
241- println ! ( " ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓ " ) ;
242- println ! ( " ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒ " ) ;
243- println ! ( " ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒ " ) ;
244- println ! ( " ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒ " ) ;
245- println ! ( " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ " ) ;
246- println ! ( " ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒ " ) ;
247- println ! ( " ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒ " ) ;
248- println ! ( " ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ " ) ;
249- println ! ( " ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒ " ) ;
250- println ! ( " ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ " ) ;
251- println ! ( " ▒▒ ▒▒ ▒▒ ▒▒ " ) ;
252- println ! ( ) ;
253- println ! ( "We hope you enjoyed learning about the various aspects of Rust!" ) ;
254- println ! (
255- "If you noticed any issues, please don't hesitate to report them to our repo."
256- ) ;
257- println ! ( "You can also contribute your own exercises to help the greater community!" ) ;
258- println ! ( ) ;
259- println ! ( "Before reporting an issue or contributing, please read our guidelines:" ) ;
260- println ! ( "https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md" ) ;
261270 }
262271 }
263272}
264273
265- fn spawn_watch_shell ( failed_exercise_hint : & Arc < Mutex < Option < String > > > ) {
274+ fn spawn_watch_shell ( failed_exercise_hint : & Arc < Mutex < Option < String > > > , should_quit : Arc < AtomicBool > ) {
266275 let failed_exercise_hint = Arc :: clone ( failed_exercise_hint) ;
267276 println ! ( "Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here." ) ;
268277 thread:: spawn ( move || loop {
269278 let mut input = String :: new ( ) ;
270279 match io:: stdin ( ) . read_line ( & mut input) {
271280 Ok ( _) => {
272281 let input = input. trim ( ) ;
273- if input. eq ( "hint" ) {
282+ if input == "hint" {
274283 if let Some ( hint) = & * failed_exercise_hint. lock ( ) . unwrap ( ) {
275284 println ! ( "{}" , hint) ;
276285 }
277- } else if input. eq ( "clear" ) {
286+ } else if input == "clear" {
278287 println ! ( "\x1B [2J\x1B [1;1H" ) ;
279288 } else if input. eq ( "quit" ) {
289+ should_quit. store ( true , Ordering :: SeqCst ) ;
280290 println ! ( "Bye!" ) ;
281- std:: process:: exit ( 0 ) ;
282291 } else if input. eq ( "help" ) {
283292 println ! ( "Commands available to you in watch mode:" ) ;
284293 println ! ( " hint - prints the current exercise's hint" ) ;
@@ -318,14 +327,20 @@ fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise {
318327 }
319328}
320329
321- fn watch ( exercises : & [ Exercise ] , verbose : bool ) -> notify:: Result < ( ) > {
330+ enum WatchStatus {
331+ Finished ,
332+ Unfinished ,
333+ }
334+
335+ fn watch ( exercises : & [ Exercise ] , verbose : bool ) -> notify:: Result < WatchStatus > {
322336 /* Clears the terminal with an ANSI escape code.
323337 Works in UNIX and newer Windows terminals. */
324338 fn clear_screen ( ) {
325339 println ! ( "\x1B c" ) ;
326340 }
327341
328342 let ( tx, rx) = channel ( ) ;
343+ let should_quit = Arc :: new ( AtomicBool :: new ( false ) ) ;
329344
330345 let mut watcher: RecommendedWatcher = Watcher :: new ( tx, Duration :: from_secs ( 2 ) ) ?;
331346 watcher. watch ( Path :: new ( "./exercises" ) , RecursiveMode :: Recursive ) ?;
@@ -334,12 +349,12 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
334349
335350 let to_owned_hint = |t : & Exercise | t. hint . to_owned ( ) ;
336351 let failed_exercise_hint = match verify ( exercises. iter ( ) , verbose) {
337- Ok ( _) => return Ok ( ( ) ) ,
352+ Ok ( _) => return Ok ( WatchStatus :: Finished ) ,
338353 Err ( exercise) => Arc :: new ( Mutex :: new ( Some ( to_owned_hint ( exercise) ) ) ) ,
339354 } ;
340- spawn_watch_shell ( & failed_exercise_hint) ;
355+ spawn_watch_shell ( & failed_exercise_hint, Arc :: clone ( & should_quit ) ) ;
341356 loop {
342- match rx. recv ( ) {
357+ match rx. recv_timeout ( Duration :: from_secs ( 1 ) ) {
343358 Ok ( event) => match event {
344359 DebouncedEvent :: Create ( b) | DebouncedEvent :: Chmod ( b) | DebouncedEvent :: Write ( b) => {
345360 if b. extension ( ) == Some ( OsStr :: new ( "rs" ) ) && b. exists ( ) {
@@ -355,7 +370,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
355370 ) ;
356371 clear_screen ( ) ;
357372 match verify ( pending_exercises, verbose) {
358- Ok ( _) => return Ok ( ( ) ) ,
373+ Ok ( _) => return Ok ( WatchStatus :: Finished ) ,
359374 Err ( exercise) => {
360375 let mut failed_exercise_hint = failed_exercise_hint. lock ( ) . unwrap ( ) ;
361376 * failed_exercise_hint = Some ( to_owned_hint ( exercise) ) ;
@@ -365,8 +380,15 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
365380 }
366381 _ => { }
367382 } ,
383+ Err ( RecvTimeoutError :: Timeout ) => {
384+ // the timeout expired, just check the `should_quit` variable below then loop again
385+ }
368386 Err ( e) => println ! ( "watch error: {:?}" , e) ,
369387 }
388+ // Check if we need to exit
389+ if should_quit. load ( Ordering :: SeqCst ) {
390+ return Ok ( WatchStatus :: Unfinished ) ;
391+ }
370392 }
371393}
372394
0 commit comments