@@ -21,6 +21,7 @@ pub use crate::data::{
2121pub use crate :: error:: { LangsError , ParamError } ;
2222pub use crate :: submission_packaging:: prepare_submission;
2323pub use crate :: submission_processing:: prepare_solution;
24+ use data:: DownloadTargetKind ;
2425use hmac:: { Hmac , NewMac } ;
2526use serde:: Serialize ;
2627use sha2:: Sha256 ;
@@ -176,7 +177,7 @@ pub fn download_or_update_course_exercises(
176177 ) ;
177178
178179 let exercises_details = client. get_exercises_details ( exercises) ?;
179- let mut projects_config = ProjectsConfig :: load ( projects_dir) ?;
180+ let projects_config = ProjectsConfig :: load ( projects_dir) ?;
180181
181182 // separate exercises into downloads and skipped
182183 let mut to_be_downloaded = vec ! [ ] ;
@@ -220,30 +221,33 @@ pub fn download_or_update_course_exercises(
220221 . max_by_key ( |s| s. created_at )
221222 {
222223 // previous submission found
223- to_be_downloaded. push ( DownloadTarget :: Submission {
224+ to_be_downloaded. push ( DownloadTarget {
224225 target : ExerciseDownload {
225226 id : exercise_detail. id ,
226227 course_slug : exercise_detail. course_name ,
227228 exercise_slug : exercise_detail. exercise_name ,
228229 path : target,
229230 } ,
230- submission_id : latest_submission. id ,
231231 checksum : exercise_detail. checksum ,
232+ kind : DownloadTargetKind :: Submission {
233+ submission_id : latest_submission. id ,
234+ } ,
232235 } ) ;
233236 continue ;
234237 }
235238 }
236239 }
237240
238241 // not skipped, either not on disk or no previous submissions, downloading template
239- to_be_downloaded. push ( DownloadTarget :: Template {
242+ to_be_downloaded. push ( DownloadTarget {
240243 target : ExerciseDownload {
241244 id : exercise_detail. id ,
242245 course_slug : exercise_detail. course_name . clone ( ) ,
243246 exercise_slug : exercise_detail. exercise_name . clone ( ) ,
244247 path : target,
245248 } ,
246249 checksum : exercise_detail. checksum ,
250+ kind : DownloadTargetKind :: Template ,
247251 } ) ;
248252 }
249253
@@ -259,9 +263,12 @@ pub fn download_or_update_course_exercises(
259263 let thread_count = to_be_downloaded. len ( ) . min ( 4 ) ; // max 4 threads
260264 let mut handles = vec ! [ ] ;
261265 let exercises = Arc :: new ( Mutex :: new ( to_be_downloaded) ) ;
266+ let projects_config = Arc :: new ( Mutex :: new ( projects_config) ) ;
262267 for _thread_id in 0 ..thread_count {
263268 let client = client. clone ( ) ;
264269 let exercises = Arc :: clone ( & exercises) ;
270+ let projects_config = Arc :: clone ( & projects_config) ;
271+ let projects_dir = projects_dir. to_path_buf ( ) ;
265272
266273 // each thread returns either a list of successful downloads, or a tuple of successful downloads and errors
267274 type ThreadErr = ( Vec < DownloadTarget > , Vec < ( DownloadTarget , LangsError ) > ) ;
@@ -284,41 +291,35 @@ pub fn download_or_update_course_exercises(
284291 let exercise_download_result = || -> Result < ( ) , LangsError > {
285292 let zip_file = file_util:: named_temp_file ( ) ?;
286293
287- let target_exercise = match download_target {
288- DownloadTarget :: Template { ref target, .. } => target,
289- DownloadTarget :: Submission { ref target, .. } => target,
290- } ;
291294 progress_reporter:: progress_stage :: < ClientUpdateData > (
292295 format ! (
293296 "Downloading exercise {} to '{}'" ,
294- target_exercise . id,
295- target_exercise . path. display( ) ,
297+ download_target . target . id,
298+ download_target . target . path. display( ) ,
296299 ) ,
297300 Some ( ClientUpdateData :: ExerciseDownload {
298- id : target_exercise . id ,
299- path : target_exercise . path . clone ( ) ,
301+ id : download_target . target . id ,
302+ path : download_target . target . path . clone ( ) ,
300303 } ) ,
301304 ) ;
302305
303- match & download_target {
304- DownloadTarget :: Template { target, .. } => {
305- client. download_exercise ( target. id , zip_file. path ( ) ) ?;
306- extract_project ( zip_file, & target. path , false ) ?;
306+ // execute download based on type
307+ match & download_target. kind {
308+ DownloadTargetKind :: Template => {
309+ client. download_exercise ( download_target. target . id , zip_file. path ( ) ) ?;
310+ extract_project ( zip_file, & download_target. target . path , false ) ?;
307311 }
308- DownloadTarget :: Submission {
309- target,
310- submission_id,
311- ..
312- } => {
313- client. download_exercise ( target. id , zip_file. path ( ) ) ?;
314- extract_project ( & zip_file, & target. path , false ) ?;
315-
316- let plugin = get_language_plugin ( & target. path ) ?;
317- let tmc_project_yml = TmcProjectYml :: load_or_default ( & target. path ) ?;
312+ DownloadTargetKind :: Submission { submission_id } => {
313+ client. download_exercise ( download_target. target . id , zip_file. path ( ) ) ?;
314+ extract_project ( & zip_file, & download_target. target . path , false ) ?;
315+
316+ let plugin = get_language_plugin ( & download_target. target . path ) ?;
317+ let tmc_project_yml =
318+ TmcProjectYml :: load_or_default ( & download_target. target . path ) ?;
318319 let config =
319320 plugin. get_exercise_packaging_configuration ( tmc_project_yml) ?;
320321 for student_file in config. student_file_paths {
321- let student_file = target. path . join ( & student_file) ;
322+ let student_file = download_target . target . path . join ( & student_file) ;
322323 log:: debug!( "student file {}" , student_file. display( ) ) ;
323324 if student_file. is_file ( ) {
324325 file_util:: remove_file ( & student_file) ?;
@@ -328,19 +329,32 @@ pub fn download_or_update_course_exercises(
328329 }
329330
330331 client. download_old_submission ( * submission_id, zip_file. path ( ) ) ?;
331- plugin. extract_student_files ( & zip_file, & target. path ) ?;
332+ plugin
333+ . extract_student_files ( & zip_file, & download_target. target . path ) ?;
332334 }
333335 }
336+ // download successful, save to course config
337+ let mut projects_config =
338+ projects_config. lock ( ) . map_err ( |_| LangsError :: MutexError ) ?; // lock mutex
339+ let course_config = projects_config
340+ . get_or_init_course_config ( download_target. target . course_slug . clone ( ) ) ;
341+ course_config. add_exercise (
342+ download_target. target . exercise_slug . clone ( ) ,
343+ download_target. target . id ,
344+ download_target. checksum . clone ( ) ,
345+ ) ;
346+ course_config. save_to_projects_dir ( & projects_dir) ?;
347+ drop ( projects_config) ; // drop mutex
334348
335349 progress_reporter:: progress_stage :: < ClientUpdateData > (
336350 format ! (
337351 "Downloaded exercise {} to '{}'" ,
338- target_exercise . id,
339- target_exercise . path. display( ) ,
352+ download_target . target . id,
353+ download_target . target . path. display( ) ,
340354 ) ,
341355 Some ( ClientUpdateData :: ExerciseDownload {
342- id : target_exercise . id ,
343- path : target_exercise . path . clone ( ) ,
356+ id : download_target . target . id ,
357+ path : download_target . target . path . clone ( ) ,
344358 } ) ,
345359 ) ;
346360
@@ -365,6 +379,7 @@ pub fn download_or_update_course_exercises(
365379 handles. push ( handle) ;
366380 }
367381
382+ // gather results from each thread
368383 let mut successful = vec ! [ ] ;
369384 let mut failed = vec ! [ ] ;
370385 for handle in handles {
@@ -377,42 +392,9 @@ pub fn download_or_update_course_exercises(
377392 }
378393 }
379394
380- log:: debug!( "save updated information to the course config" ) ;
381- // turn the downloaded exercises into a hashmap with the course as key
382- let mut course_data = HashMap :: < String , Vec < ( String , String , usize ) > > :: new ( ) ;
383- for download_target in & successful {
384- let ( target, checksum) = match download_target {
385- DownloadTarget :: Submission {
386- target, checksum, ..
387- }
388- | DownloadTarget :: Template {
389- target, checksum, ..
390- } => ( target, checksum) ,
391- } ;
392- let entry = course_data. entry ( target. course_slug . clone ( ) ) ;
393- let course_exercises = entry. or_default ( ) ;
394- course_exercises. push ( ( target. exercise_slug . clone ( ) , checksum. clone ( ) , target. id ) ) ;
395- }
396- // update/create the course configs that contain downloaded or updated exercises
397- for ( course_name, exercise_names) in course_data {
398- let exercises = exercise_names
399- . into_iter ( )
400- . map ( |( name, checksum, id) | ( name, ProjectsDirExercise { id, checksum } ) )
401- . collect ( ) ;
402- if let Some ( course_config) = projects_config. courses . get_mut ( & course_name) {
403- course_config. exercises . extend ( exercises) ;
404- course_config. save_to_projects_dir ( projects_dir) ?;
405- } else {
406- let course_config = CourseConfig {
407- course : course_name,
408- exercises,
409- } ;
410- course_config. save_to_projects_dir ( projects_dir) ?;
411- } ;
412- }
413-
395+ // report
414396 let finish_message = if failed. is_empty ( ) {
415- if successful. len ( ) == 0 && exercises_len == 0 {
397+ if successful. is_empty ( ) && exercises_len == 0 {
416398 "Exercises are already up-to-date!" . to_string ( )
417399 } else {
418400 format ! (
@@ -429,18 +411,10 @@ pub fn download_or_update_course_exercises(
429411 failed. len( ) ,
430412 )
431413 } ;
432-
433414 progress_reporter:: finish_stage :: < ClientUpdateData > ( finish_message, None ) ;
434415
435- let downloaded = successful
436- . into_iter ( )
437- . map ( |t| match t {
438- DownloadTarget :: Submission { target, .. } => target,
439- DownloadTarget :: Template { target, .. } => target,
440- } )
441- . collect ( ) ;
442-
443- // return an error if any downloads failed
416+ // return information about the downloads
417+ let downloaded = successful. into_iter ( ) . map ( |t| t. target ) . collect ( ) ;
444418 if !failed. is_empty ( ) {
445419 // add an error trace to each failed download
446420 let failed = failed
@@ -452,11 +426,7 @@ pub fn download_or_update_course_exercises(
452426 chain. push ( source. to_string ( ) ) ;
453427 error = source;
454428 }
455- let target = match target {
456- DownloadTarget :: Submission { target, .. }
457- | DownloadTarget :: Template { target, .. } => target,
458- } ;
459- ( target, chain)
429+ ( target. target , chain)
460430 } )
461431 . collect ( ) ;
462432 return Ok ( DownloadResult :: Failure {
0 commit comments