Skip to content

Commit fdb34c2

Browse files
committed
added more data to download results, made the types more similar, refactored dl
1 parent b1d8d0a commit fdb34c2

File tree

4 files changed

+137
-83
lines changed

4 files changed

+137
-83
lines changed

tmc-langs-cli/api/output-data-download-or-update.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@
88
"downloaded": [
99
{
1010
"course-slug": "some course",
11-
"exercise-slug": "some exercise"
11+
"exercise-slug": "some exercise",
12+
"path": "some path"
1213
},
1314
{
1415
"course-slug": "some course",
15-
"exercise-slug": "another exercise"
16+
"exercise-slug": "another exercise",
17+
"path": "another path"
1618
}
1719
],
1820
"skipped": [
1921
{
2022
"course-slug": "another course",
21-
"exercise-slug": "some skipped exercise"
23+
"exercise-slug": "some skipped exercise",
24+
"path": "third path"
2225
}
2326
]
2427
}

tmc-langs-cli/src/error.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::path::PathBuf;
22
use thiserror::Error;
33

4+
use crate::output::DownloadOrUpdateCourseExercise;
5+
46
#[derive(Debug, Error)]
57
#[error("Invalid token. Deleted credentials file")]
68
pub struct InvalidTokenError {
@@ -17,7 +19,7 @@ pub struct SandboxTestError {
1719
#[derive(Debug, Error)]
1820
#[error("Failed to download one or more exercises")]
1921
pub struct DownloadsFailedError {
20-
pub completed: Vec<usize>,
21-
pub skipped: Vec<usize>,
22-
pub failed: Vec<(usize, Vec<String>)>,
22+
pub downloaded: Vec<DownloadOrUpdateCourseExercise>,
23+
pub skipped: Vec<DownloadOrUpdateCourseExercise>,
24+
pub failed: Vec<(DownloadOrUpdateCourseExercise, Vec<String>)>,
2325
}

tmc-langs-cli/src/main.rs

Lines changed: 112 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)