@@ -122,7 +122,7 @@ fn solve_error_kind(e: &anyhow::Error) -> Kind {
122122
123123 // check for download failed error
124124 if let Some ( DownloadsFailedError {
125- completed,
125+ downloaded : completed,
126126 skipped,
127127 failed,
128128 } ) = cause. downcast_ref :: < DownloadsFailedError > ( )
@@ -868,11 +868,16 @@ fn run_core(
868868 let projects_dir = TmcConfig :: load ( client_name) ?. projects_dir ;
869869 let mut projects_config = ProjectsConfig :: load ( & projects_dir) ?;
870870
871- let mut course_data = HashMap :: < String , Vec < ( String , String , usize ) > > :: new ( ) ;
872- let mut exercises_and_paths = vec ! [ ] ;
873- let mut downloaded = vec ! [ ] ;
874- let mut skipped = vec ! [ ] ;
871+ // separate downloads into ones that don't need to be downloaded and ones that do
872+ let mut to_be_downloaded = HashMap :: new ( ) ;
873+ let mut to_be_skipped = vec ! [ ] ;
875874 for exercise_detail in exercises_details {
875+ let target = ProjectsConfig :: get_exercise_download_target (
876+ & projects_dir,
877+ & exercise_detail. course_name ,
878+ & exercise_detail. exercise_name ,
879+ ) ;
880+
876881 // check if the checksum is different from what's already on disk
877882 if let Some ( course_config) =
878883 projects_config. courses . get ( & exercise_detail. course_name )
@@ -888,68 +893,84 @@ fn run_core(
888893 exercise_detail. course_name,
889894 exercise_detail. exercise_name
890895 ) ;
891- skipped . push ( DownloadOrUpdateCourseExercise {
896+ to_be_skipped . push ( DownloadOrUpdateCourseExercise {
892897 course_slug : exercise_detail. course_name ,
893898 exercise_slug : exercise_detail. exercise_name ,
899+ path : target,
894900 } ) ;
895901 continue ;
896902 }
897903 }
898904 }
899- // not skipped, will be downloaded
900- // if any download fails, an error is returned instead, so it's ok to just push them to downloaded here
901- downloaded. push ( DownloadOrUpdateCourseExercise {
902- course_slug : exercise_detail. course_name . clone ( ) ,
903- exercise_slug : exercise_detail. exercise_name . clone ( ) ,
904- } ) ;
905-
906- let target = ProjectsConfig :: get_exercise_download_target (
907- & projects_dir,
908- & exercise_detail. course_name ,
909- & exercise_detail. exercise_name ,
910- ) ;
911-
912- let entry = course_data. entry ( exercise_detail. course_name ) ;
913- let course_exercises = entry. or_default ( ) ;
914- course_exercises. push ( (
915- exercise_detail. exercise_name ,
916- exercise_detail. checksum ,
905+ // not skipped, should be downloaded
906+ // also store id and checksum to be used later
907+ to_be_downloaded. insert (
917908 exercise_detail. id ,
918- ) ) ;
919-
920- exercises_and_paths. push ( ( exercise_detail. id , target) ) ;
909+ (
910+ DownloadOrUpdateCourseExercise {
911+ course_slug : exercise_detail. course_name . clone ( ) ,
912+ exercise_slug : exercise_detail. exercise_name . clone ( ) ,
913+ path : target,
914+ } ,
915+ exercise_detail. id ,
916+ exercise_detail. checksum ,
917+ ) ,
918+ ) ;
921919 }
922920
923- if let Err ( error) = client. download_or_update_exercises ( exercises_and_paths) {
924- // check for incomplete download result
925- if let ClientError :: IncompleteDownloadResult { downloaded, failed } = error {
926- anyhow:: bail!( DownloadsFailedError {
927- completed: downloaded,
928- skipped: vec![ ] ,
929- failed: failed
930- . into_iter( )
931- . map( |e| {
932- let mut error = & e. 1 as & dyn StdError ;
933- let mut chain = vec![ error. to_string( ) ] ;
934- while let Some ( source) = error. source( ) {
935- chain. push( source. to_string( ) ) ;
936- error = source;
937- }
938- ( e. 0 , chain)
939- } )
940- . collect( ) ,
941- } )
942- } else {
921+ // download and divide the results into successful and failed downloads
922+ let exercises_and_paths = to_be_downloaded
923+ . iter ( )
924+ . map ( |( id, ( ex, ..) ) | ( * id, ex. path . clone ( ) ) )
925+ . collect ( ) ;
926+ let download_result = client. download_or_update_exercises ( exercises_and_paths) ;
927+ let ( downloaded, failed) = match download_result {
928+ Ok ( _) => {
929+ let downloaded = to_be_downloaded. into_iter ( ) . map ( |( k, v) | v) . collect ( ) ;
930+ let failed = vec ! [ ] ;
931+ ( downloaded, failed)
932+ }
933+ Err ( ClientError :: IncompleteDownloadResult { downloaded, failed } ) => {
934+ let downloaded = downloaded
935+ . iter ( )
936+ . map ( |id| to_be_downloaded. remove ( id) . unwrap ( ) )
937+ . collect :: < Vec < _ > > ( ) ;
938+ let failed = failed
939+ . into_iter ( )
940+ . map ( |( id, e) | ( to_be_downloaded. remove ( & id) . unwrap ( ) , e) )
941+ . collect :: < Vec < _ > > ( ) ;
942+ ( downloaded, failed)
943+ }
944+ Err ( error) => {
943945 anyhow:: bail!( error)
944946 }
945- }
947+ } ;
946948
947- for ( course_name, exercise_names) in course_data {
948- let mut exercises = BTreeMap :: new ( ) ;
949- for ( exercise_name, checksum, id) in exercise_names {
950- exercises. insert ( exercise_name, Exercise { id, checksum } ) ;
951- }
949+ /*
950+ let entry = course_data.entry(exercise_detail.course_name);
951+ let course_exercises = entry.or_default();
952+ course_exercises.push((
953+ exercise_detail.exercise_name,
954+ exercise_detail.checksum,
955+ exercise_detail.id,
956+ ));
952957
958+ exercises_and_paths.push((exercise_detail.id, target));
959+ */
960+
961+ // turn the downloaded exercises into a hashmap with the course as key
962+ let mut course_data = HashMap :: < String , Vec < ( String , String , usize ) > > :: new ( ) ;
963+ for ( download, id, checksum) in & downloaded {
964+ let entry = course_data. entry ( download. course_slug . clone ( ) ) ;
965+ let course_exercises = entry. or_default ( ) ;
966+ course_exercises. push ( ( download. exercise_slug . clone ( ) , checksum. clone ( ) , * id) ) ;
967+ }
968+ // update/create the course configs that contain downloaded or updated exercises
969+ for ( course_name, exercise_names) in course_data {
970+ let exercises = exercise_names
971+ . into_iter ( )
972+ . map ( |( name, checksum, id) | ( name, Exercise { id, checksum } ) )
973+ . collect ( ) ;
953974 if let Some ( course_config) = projects_config. courses . get_mut ( & course_name) {
954975 course_config. exercises . extend ( exercises) ;
955976 course_config. save_to_projects_dir ( & projects_dir) ?;
@@ -962,9 +983,32 @@ fn run_core(
962983 } ;
963984 }
964985
986+ let completed = downloaded. into_iter ( ) . map ( |d| d. 0 ) . collect ( ) ;
987+ // return an error if any downloads failed
988+ if !failed. is_empty ( ) {
989+ // add an error trace to each failed download
990+ let failed = failed
991+ . into_iter ( )
992+ . map ( |( ( ex, id, checksum) , err) | {
993+ let mut error = & err as & dyn StdError ;
994+ let mut chain = vec ! [ error. to_string( ) ] ;
995+ while let Some ( source) = error. source ( ) {
996+ chain. push ( source. to_string ( ) ) ;
997+ error = source;
998+ }
999+ ( ex, chain)
1000+ } )
1001+ . collect ( ) ;
1002+ anyhow:: bail!( DownloadsFailedError {
1003+ downloaded: completed,
1004+ skipped: to_be_skipped,
1005+ failed,
1006+ } )
1007+ }
1008+
9651009 let data = DownloadOrUpdateCourseExercisesResult {
966- downloaded,
967- skipped,
1010+ downloaded : completed ,
1011+ skipped : to_be_skipped ,
9681012 } ;
9691013 let output = Output :: finished_with_data (
9701014 "downloaded or updated exercises" ,
@@ -1395,8 +1439,8 @@ fn run_core(
13951439 }
13961440 ( "update-exercises" , Some ( _) ) => {
13971441 let mut exercises_to_update = vec ! [ ] ;
1398- let mut downloaded_exercises = vec ! [ ] ;
1399- let mut skipped_exercises = vec ! [ ] ;
1442+ let mut to_be_downloaded = vec ! [ ] ;
1443+ let mut to_be_skipped = vec ! [ ] ;
14001444 let mut course_data = HashMap :: < String , Vec < ( String , String , usize ) > > :: new ( ) ;
14011445
14021446 let projects_dir = TmcConfig :: load ( client_name) ?. projects_dir ;
@@ -1425,13 +1469,13 @@ fn run_core(
14251469 local_exercise. id
14261470 )
14271471 } ) ?;
1472+ let target = ProjectsConfig :: get_exercise_download_target (
1473+ & projects_dir,
1474+ & server_exercise. course_name ,
1475+ & server_exercise. exercise_name ,
1476+ ) ;
14281477 if server_exercise. checksum != local_exercise. checksum {
14291478 // server has an updated exercise
1430- let target = ProjectsConfig :: get_exercise_download_target (
1431- & projects_dir,
1432- & server_exercise. course_name ,
1433- & server_exercise. exercise_name ,
1434- ) ;
14351479 let exercise_list = course_data
14361480 . entry ( server_exercise. course_name . clone ( ) )
14371481 . or_default ( ) ;
@@ -1440,15 +1484,16 @@ fn run_core(
14401484 server_exercise. checksum . clone ( ) ,
14411485 server_exercise. id ,
14421486 ) ) ;
1443- exercises_to_update. push ( ( local_exercise. id , target) ) ;
1444- downloaded_exercises. push ( DownloadOrUpdateCourseExercise {
1487+ to_be_downloaded. push ( DownloadOrUpdateCourseExercise {
14451488 course_slug : server_exercise. course_name . clone ( ) ,
14461489 exercise_slug : server_exercise. exercise_name . clone ( ) ,
1490+ path : target,
14471491 } ) ;
14481492 } else {
1449- skipped_exercises . push ( DownloadOrUpdateCourseExercise {
1493+ to_be_skipped . push ( DownloadOrUpdateCourseExercise {
14501494 course_slug : server_exercise. course_name . clone ( ) ,
14511495 exercise_slug : server_exercise. exercise_name . clone ( ) ,
1496+ path : target,
14521497 } ) ;
14531498 }
14541499 }
@@ -1477,8 +1522,8 @@ fn run_core(
14771522 }
14781523
14791524 let data = DownloadOrUpdateCourseExercisesResult {
1480- downloaded : downloaded_exercises ,
1481- skipped : skipped_exercises ,
1525+ downloaded : to_be_downloaded ,
1526+ skipped : to_be_skipped ,
14821527 } ;
14831528 let output = Output :: finished_with_data (
14841529 "downloaded or updated exercises" ,
0 commit comments