1
1
use anyhow:: { bail, Context , Error , Result } ;
2
- use crossterm:: {
3
- style:: { ResetColor , SetForegroundColor } ,
4
- terminal, QueueableCommand ,
5
- } ;
2
+ use crossterm:: { cursor, terminal, QueueableCommand } ;
6
3
use std:: {
7
4
env,
8
5
fs:: { File , OpenOptions } ,
@@ -23,7 +20,7 @@ use crate::{
23
20
embedded:: EMBEDDED_FILES ,
24
21
exercise:: { Exercise , RunnableExercise } ,
25
22
info_file:: ExerciseInfo ,
26
- term:: { self , progress_bar_with_success } ,
23
+ term:: { self , show_exercises_check_progress } ,
27
24
} ;
28
25
29
26
const STATE_FILE_NAME : & str = ".rustlings-state.txt" ;
@@ -44,18 +41,12 @@ pub enum StateFileStatus {
44
41
NotRead ,
45
42
}
46
43
47
- enum ExerciseCheckProgress {
48
- Checking ,
49
- Done ,
50
- Pending ,
51
- Error ,
52
- }
53
-
54
44
#[ derive( Clone , Copy ) ]
55
- enum ExerciseCheckResult {
45
+ pub enum ExerciseCheckProgress {
46
+ None ,
47
+ Checking ,
56
48
Done ,
57
49
Pending ,
58
- Error ,
59
50
}
60
51
61
52
pub struct AppState {
@@ -417,27 +408,25 @@ impl AppState {
417
408
}
418
409
}
419
410
420
- // Return the exercise index of the first pending exercise found.
421
- pub fn check_all_exercises ( & mut self , stdout : & mut StdoutLock ) -> Result < Option < usize > > {
411
+ fn check_all_exercises_impl ( & mut self , stdout : & mut StdoutLock ) -> Result < Option < usize > > {
422
412
stdout. write_all ( "Checking all exercises…\n " . as_bytes ( ) ) ?;
423
- let n_exercises = self . exercises . len ( ) as u16 ;
424
413
let next_exercise_ind = AtomicUsize :: new ( 0 ) ;
425
414
let term_width = terminal:: size ( )
426
415
. context ( "Failed to get the terminal size" ) ?
427
416
. 0 ;
417
+ clear_terminal ( stdout) ?;
428
418
429
- let mut results = vec ! [ ExerciseCheckResult :: Error ; self . exercises. len( ) ] ;
419
+ let mut progresses = vec ! [ ExerciseCheckProgress :: None ; self . exercises. len( ) ] ;
430
420
let mut done = 0 ;
431
421
let mut pending = 0 ;
432
422
433
423
thread:: scope ( |s| {
434
- let mut checking = 0 ;
435
- let ( exercise_result_sender, exercise_result_receiver) = mpsc:: channel ( ) ;
424
+ let ( exercise_progress_sender, exercise_progress_receiver) = mpsc:: channel ( ) ;
436
425
let n_threads = thread:: available_parallelism ( )
437
426
. map_or ( DEFAULT_CHECK_PARALLELISM , |count| count. get ( ) ) ;
438
427
439
428
for _ in 0 ..n_threads {
440
- let exercise_result_sender = exercise_result_sender . clone ( ) ;
429
+ let exercise_progress_sender = exercise_progress_sender . clone ( ) ;
441
430
let next_exercise_ind = & next_exercise_ind;
442
431
let slf = & self ;
443
432
thread:: Builder :: new ( )
@@ -449,125 +438,102 @@ impl AppState {
449
438
} ;
450
439
451
440
// Notify the progress bar that this exercise is pending.
452
- if exercise_result_sender
441
+ if exercise_progress_sender
453
442
. send ( ( exercise_ind, ExerciseCheckProgress :: Checking ) )
454
443
. is_err ( )
455
444
{
456
445
break ;
457
446
} ;
458
447
459
448
let success = exercise. run_exercise ( None , & slf. cmd_runner ) ;
460
- let result = match success {
449
+ let progress = match success {
461
450
Ok ( true ) => ExerciseCheckProgress :: Done ,
462
451
Ok ( false ) => ExerciseCheckProgress :: Pending ,
463
- Err ( _) => ExerciseCheckProgress :: Error ,
452
+ Err ( _) => ExerciseCheckProgress :: None ,
464
453
} ;
465
454
466
455
// Notify the progress bar that this exercise is done.
467
- if exercise_result_sender. send ( ( exercise_ind, result) ) . is_err ( ) {
456
+ if exercise_progress_sender
457
+ . send ( ( exercise_ind, progress) )
458
+ . is_err ( )
459
+ {
468
460
break ;
469
461
}
470
462
} )
471
463
. context ( "Failed to spawn a thread to check all exercises" ) ?;
472
464
}
473
465
474
466
// Drop this sender to detect when the last thread is done.
475
- drop ( exercise_result_sender) ;
476
-
477
- // Print the legend.
478
- stdout. write_all ( b"Color legend: " ) ?;
479
- stdout. queue ( SetForegroundColor ( term:: PROGRESS_FAILED_COLOR ) ) ?;
480
- stdout. write_all ( b"Pending" ) ?;
481
- stdout. queue ( ResetColor ) ?;
482
- stdout. write_all ( b" - " ) ?;
483
- stdout. queue ( SetForegroundColor ( term:: PROGRESS_SUCCESS_COLOR ) ) ?;
484
- stdout. write_all ( b"Done" ) ?;
485
- stdout. queue ( ResetColor ) ?;
486
- stdout. write_all ( b" - " ) ?;
487
- stdout. queue ( SetForegroundColor ( term:: PROGRESS_PENDING_COLOR ) ) ?;
488
- stdout. write_all ( b"Checking" ) ?;
489
- stdout. queue ( ResetColor ) ?;
490
- stdout. write_all ( b"\n " ) ?;
467
+ drop ( exercise_progress_sender) ;
491
468
492
- while let Ok ( ( exercise_ind, result) ) = exercise_result_receiver. recv ( ) {
493
- match result {
494
- ExerciseCheckProgress :: Checking => checking += 1 ,
495
- ExerciseCheckProgress :: Done => {
496
- results[ exercise_ind] = ExerciseCheckResult :: Done ;
497
- checking -= 1 ;
498
- done += 1 ;
499
- }
500
- ExerciseCheckProgress :: Pending => {
501
- results[ exercise_ind] = ExerciseCheckResult :: Pending ;
502
- checking -= 1 ;
503
- pending += 1 ;
504
- }
505
- ExerciseCheckProgress :: Error => checking -= 1 ,
469
+ while let Ok ( ( exercise_ind, progress) ) = exercise_progress_receiver. recv ( ) {
470
+ progresses[ exercise_ind] = progress;
471
+
472
+ match progress {
473
+ ExerciseCheckProgress :: None | ExerciseCheckProgress :: Checking => ( ) ,
474
+ ExerciseCheckProgress :: Done => done += 1 ,
475
+ ExerciseCheckProgress :: Pending => pending += 1 ,
506
476
}
507
477
508
- stdout. write_all ( b"\r " ) ?;
509
- progress_bar_with_success (
510
- stdout,
511
- checking,
512
- pending,
513
- done,
514
- n_exercises,
515
- term_width,
516
- ) ?;
517
- stdout. flush ( ) ?;
478
+ show_exercises_check_progress ( stdout, & progresses, term_width) ?;
518
479
}
519
480
520
481
Ok :: < _ , Error > ( ( ) )
521
482
} ) ?;
522
483
523
484
let mut first_pending_exercise_ind = None ;
524
- for ( exercise_ind, result ) in results . into_iter ( ) . enumerate ( ) {
525
- match result {
526
- ExerciseCheckResult :: Done => {
485
+ for exercise_ind in 0 ..progresses . len ( ) {
486
+ match progresses [ exercise_ind ] {
487
+ ExerciseCheckProgress :: Done => {
527
488
self . set_status ( exercise_ind, true ) ?;
528
489
}
529
- ExerciseCheckResult :: Pending => {
490
+ ExerciseCheckProgress :: Pending => {
530
491
self . set_status ( exercise_ind, false ) ?;
531
492
if first_pending_exercise_ind. is_none ( ) {
532
493
first_pending_exercise_ind = Some ( exercise_ind) ;
533
494
}
534
495
}
535
- ExerciseCheckResult :: Error => {
496
+ ExerciseCheckProgress :: None | ExerciseCheckProgress :: Checking => {
536
497
// If we got an error while checking all exercises in parallel,
537
498
// it could be because we exceeded the limit of open file descriptors.
538
499
// Therefore, try running exercises with errors sequentially.
500
+ progresses[ exercise_ind] = ExerciseCheckProgress :: Checking ;
501
+ show_exercises_check_progress ( stdout, & progresses, term_width) ?;
502
+
539
503
let exercise = & self . exercises [ exercise_ind] ;
540
504
let success = exercise. run_exercise ( None , & self . cmd_runner ) ?;
541
505
if success {
542
506
done += 1 ;
507
+ progresses[ exercise_ind] = ExerciseCheckProgress :: Done ;
543
508
} else {
544
509
pending += 1 ;
545
510
if first_pending_exercise_ind. is_none ( ) {
546
511
first_pending_exercise_ind = Some ( exercise_ind) ;
547
512
}
513
+ progresses[ exercise_ind] = ExerciseCheckProgress :: Pending ;
548
514
}
549
515
self . set_status ( exercise_ind, success) ?;
550
516
551
- stdout. write_all ( b"\r " ) ?;
552
- progress_bar_with_success (
553
- stdout,
554
- u16:: from ( pending + done < n_exercises) ,
555
- pending,
556
- done,
557
- n_exercises,
558
- term_width,
559
- ) ?;
560
- stdout. flush ( ) ?;
517
+ show_exercises_check_progress ( stdout, & progresses, term_width) ?;
561
518
}
562
519
}
563
520
}
564
521
565
522
self . write ( ) ?;
566
- stdout. write_all ( b"\n \n " ) ?;
523
+ stdout. write_all ( b"\n " ) ?;
567
524
568
525
Ok ( first_pending_exercise_ind)
569
526
}
570
527
528
+ // Return the exercise index of the first pending exercise found.
529
+ pub fn check_all_exercises ( & mut self , stdout : & mut StdoutLock ) -> Result < Option < usize > > {
530
+ stdout. queue ( cursor:: Hide ) ?;
531
+ let res = self . check_all_exercises_impl ( stdout) ;
532
+ stdout. queue ( cursor:: Show ) ?;
533
+
534
+ res
535
+ }
536
+
571
537
/// Mark the current exercise as done and move on to the next pending exercise if one exists.
572
538
/// If all exercises are marked as done, run all of them to make sure that they are actually
573
539
/// done. If an exercise which is marked as done fails, mark it as pending and continue on it.
0 commit comments