Skip to content

Commit 1ee7b54

Browse files
authored
Merge pull request #135 from rage/refresh-return-tmcprojectyml
return tmcproject.yml for each exercise after course refresh
2 parents bb61fb7 + 02c2b46 commit 1ee7b54

File tree

12 files changed

+101
-50
lines changed

12 files changed

+101
-50
lines changed

plugins/make/src/plugin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ impl LanguagePlugin for MakePlugin {
211211
let base_test_path = path.join("test");
212212

213213
// fails on valgrind by default
214-
let fail_on_valgrind_error = match TmcProjectYml::from(&path) {
214+
let fail_on_valgrind_error = match TmcProjectYml::load_or_default(&path) {
215215
Ok(parsed) => parsed.fail_on_valgrind_error.unwrap_or(true),
216216
Err(_) => true,
217217
};

plugins/python3/src/plugin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ impl Python3Plugin {
106106
timeout: Option<Duration>,
107107
stdin: Option<String>,
108108
) -> Result<Output, PythonError> {
109-
let minimum_python_version = TmcProjectYml::from(path)?
109+
let minimum_python_version = TmcProjectYml::load_or_default(path)?
110110
.minimum_python_version
111111
.unwrap_or_default();
112112
// default minimum version is 3.0.0

tmc-langs-cli/src/app.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -780,10 +780,12 @@ mod base_test {
780780
]);
781781
}
782782

783-
// #[test]
783+
/*
784+
#[test]
784785
fn disk_space() {
785786
get_matches(&["disk-space", "--path", "path"]);
786787
}
788+
*/
787789

788790
#[test]
789791
fn extract_project() {

tmc-langs-framework/src/plugin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ pub trait LanguagePlugin {
409409

410410
/// Parses exercise files using Self::LINE_COMMENT and Self::BLOCK_COMMENt to filter out comments and Self::points_parser to parse points from the actual code.
411411
fn get_available_points(exercise_path: &Path) -> Result<Vec<String>, TmcError> {
412-
let config = TmcProjectYml::from(exercise_path)?;
412+
let config = TmcProjectYml::load_or_default(exercise_path)?;
413413
let config = Self::get_exercise_packaging_configuration(config)?;
414414

415415
let mut points = Vec::new();

tmc-langs-framework/src/policy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub trait StudentFilePolicy {
2121
where
2222
Self: Sized,
2323
{
24-
let project_config = TmcProjectYml::from(project_dir)?;
24+
let project_config = TmcProjectYml::load_or_default(project_dir)?;
2525
Ok(Self::new_with_project_config(project_config))
2626
}
2727

tmc-langs-framework/src/tmc_project_yml.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,32 @@ use tmc_langs_util::{file_util, FileError};
1515
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
1616
pub struct TmcProjectYml {
1717
#[serde(default)]
18+
#[serde(skip_serializing_if = "Vec::is_empty")]
1819
pub extra_student_files: Vec<PathBuf>,
1920

2021
#[serde(default)]
22+
#[serde(skip_serializing_if = "Vec::is_empty")]
2123
pub extra_exercise_files: Vec<PathBuf>,
2224

2325
#[serde(default)]
26+
#[serde(skip_serializing_if = "Vec::is_empty")]
2427
pub force_update: Vec<PathBuf>,
2528

2629
#[serde(default)]
30+
#[serde(skip_serializing_if = "Option::is_none")]
2731
pub tests_timeout_ms: Option<u64>,
2832

2933
#[serde(default)]
3034
#[serde(rename = "no-tests")]
35+
#[serde(skip_serializing_if = "Option::is_none")]
3136
pub no_tests: Option<NoTests>,
3237

3338
#[serde(default)]
39+
#[serde(skip_serializing_if = "Option::is_none")]
3440
pub fail_on_valgrind_error: Option<bool>,
3541

3642
#[serde(default)]
43+
#[serde(skip_serializing_if = "Option::is_none")]
3744
pub minimum_python_version: Option<PythonVer>,
3845
}
3946

@@ -42,18 +49,28 @@ impl TmcProjectYml {
4249
dir.join(".tmcproject.yml")
4350
}
4451

45-
pub fn from(project_dir: &Path) -> Result<Self, TmcError> {
46-
let config_path = Self::path_in_dir(project_dir);
52+
/// Tries to load a
53+
pub fn load_or_default(project_dir: &Path) -> Result<Self, TmcError> {
54+
if let Some(config) = Self::load(project_dir)? {
55+
Ok(config)
56+
} else {
57+
Ok(Self::default())
58+
}
59+
}
60+
61+
pub fn load(project_dir: &Path) -> Result<Option<Self>, TmcError> {
62+
let mut config_path = project_dir.to_owned();
63+
config_path.push(".tmcproject.yml");
4764

4865
if !config_path.exists() {
4966
log::trace!("no config found at {}", config_path.display());
50-
return Ok(Self::default());
67+
return Ok(None);
5168
}
5269
log::debug!("reading .tmcproject.yml from {}", config_path.display());
5370
let file = file_util::open_file(&config_path)?;
5471
let config = serde_yaml::from_reader(file)?;
5572
log::trace!("read {:#?}", config);
56-
Ok(config)
73+
Ok(Some(config))
5774
}
5875

5976
/// Merges the contents of `with` with `self`.
@@ -286,7 +303,7 @@ mod test {
286303
tpy.save_to_dir(temp.path()).unwrap();
287304

288305
assert!(path.exists());
289-
let tpy = TmcProjectYml::from(temp.path()).unwrap();
306+
let tpy = TmcProjectYml::load(temp.path()).unwrap().unwrap();
290307
assert_eq!(tpy.tests_timeout_ms, Some(1234));
291308
}
292309
}

tmc-langs/exa/.tmcproject.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
tests_timeout_ms: 2345

tmc-langs/exb/.tmcproject.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
fail_on_valgrind_error: false

tmc-langs/src/course_refresher.rs

Lines changed: 62 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ pub struct RefreshData {
2727

2828
/// An exercise from a finished course refresh.
2929
#[derive(Debug, Serialize, Deserialize)]
30+
#[serde(rename_all = "kebab-case")]
3031
pub struct RefreshExercise {
3132
name: String,
3233
checksum: String,
3334
points: Vec<String>,
3435
#[serde(skip)]
3536
path: PathBuf,
37+
tmcproject_yml: Option<TmcProjectYml>,
3638
}
3739

3840
/// Used by tmc-server. Refreshes the course.
@@ -86,15 +88,15 @@ pub fn refresh_course(
8688
.map(|ed| ed.strip_prefix(&new_clone_path).unwrap().to_path_buf()) // safe
8789
.collect::<Vec<_>>();
8890

89-
// merge the root config with each exercise's, if any
90-
if let Ok(root_tmcproject) = TmcProjectYml::from(&course_cache_path) {
91-
merge_tmcproject_configs(root_tmcproject, &exercise_dirs)?;
92-
}
91+
// collect .tmcproject.ymls and merge the root config with each exercise's, if any
92+
let root_tmcproject_yml = TmcProjectYml::load(&course_cache_path)?;
93+
let exercise_dirs_and_tmcprojects =
94+
get_and_merge_tmcproject_configs(root_tmcproject_yml, &new_clone_path, exercise_dirs)?;
9395
progress_stage("Merged .tmcproject.yml files in exercise directories to the root file, if any");
9496

9597
// make_solutions
9698
log::info!("preparing solutions to {}", new_solution_path.display());
97-
for exercise in &exercise_dirs {
99+
for (exercise, _) in &exercise_dirs_and_tmcprojects {
98100
super::prepare_solution(
99101
&new_clone_path.join(&exercise),
100102
&new_solution_path.join(&exercise),
@@ -104,15 +106,19 @@ pub fn refresh_course(
104106

105107
// make_stubs
106108
log::info!("preparing stubs to {}", new_stub_path.display());
107-
for exercise in &exercise_dirs {
109+
for (exercise, _) in &exercise_dirs_and_tmcprojects {
108110
super::prepare_stub(
109111
&new_clone_path.join(&exercise),
110112
&new_stub_path.join(&exercise),
111113
)?;
112114
}
113115
progress_stage("Prepared stubs");
114116

115-
let exercises = get_exercises(exercise_dirs, &new_clone_path, &new_stub_path)?;
117+
let exercises = get_exercises(
118+
exercise_dirs_and_tmcprojects,
119+
&new_clone_path,
120+
&new_stub_path,
121+
)?;
116122
progress_stage("Located exercises");
117123

118124
// make_zips_of_solutions
@@ -221,17 +227,30 @@ fn check_directory_names(path: &Path) -> Result<(), LangsError> {
221227
Ok(())
222228
}
223229

224-
fn merge_tmcproject_configs(
225-
root_tmcproject: TmcProjectYml,
226-
exercise_dirs: &[PathBuf],
227-
) -> Result<(), LangsError> {
230+
fn get_and_merge_tmcproject_configs(
231+
root_tmcproject: Option<TmcProjectYml>,
232+
clone_path: &Path,
233+
exercise_dirs: Vec<PathBuf>,
234+
) -> Result<Vec<(PathBuf, Option<TmcProjectYml>)>, LangsError> {
235+
let mut res = vec![];
228236
for exercise_dir in exercise_dirs {
229-
if let Ok(mut exercise_tmcproject) = TmcProjectYml::from(exercise_dir) {
230-
exercise_tmcproject.merge(root_tmcproject.clone());
231-
exercise_tmcproject.save_to_dir(exercise_dir)?;
237+
let target_dir = clone_path.join(&exercise_dir);
238+
let exercise_tmcproject = TmcProjectYml::load(&target_dir)?;
239+
match (&root_tmcproject, exercise_tmcproject) {
240+
(Some(root), Some(mut exercise)) => {
241+
exercise.merge(root.clone());
242+
exercise.save_to_dir(&target_dir)?;
243+
res.push((exercise_dir, Some(exercise)));
244+
}
245+
(Some(root), None) => {
246+
root.save_to_dir(&target_dir)?;
247+
res.push((exercise_dir, Some(root.clone())));
248+
}
249+
(None, Some(exercise)) => res.push((exercise_dir, Some(exercise))),
250+
(None, None) => res.push((exercise_dir, None)),
232251
}
233252
}
234-
Ok(())
253+
Ok(res)
235254
}
236255

237256
/// Checks for a course_clone_path/course_options.yml
@@ -257,15 +276,15 @@ fn get_course_options(course_clone_path: &Path, course_name: &str) -> Result<Map
257276
/// Finds exercise directories, and converts the directories to "exercise names" by swapping the separators for dashes.
258277
/// Also calculates checksums and fetches points for all
259278
fn get_exercises(
260-
exercise_dirs: Vec<PathBuf>,
279+
exercise_dirs_and_tmcprojects: Vec<(PathBuf, Option<TmcProjectYml>)>,
261280
course_clone_path: &Path,
262281
course_stub_path: &Path,
263282
) -> Result<Vec<RefreshExercise>, LangsError> {
264283
log::info!("finding exercise checksums and points");
265284

266-
let exercises = exercise_dirs
285+
let exercises = exercise_dirs_and_tmcprojects
267286
.into_iter()
268-
.map(|exercise_dir| {
287+
.map(|(exercise_dir, tmcproject_yml)| {
269288
log::debug!(
270289
"processing points and checksum for {}",
271290
exercise_dir.display()
@@ -280,6 +299,7 @@ fn get_exercises(
280299
points,
281300
checksum,
282301
path: exercise_dir,
302+
tmcproject_yml,
283303
})
284304
})
285305
.collect::<Result<_, LangsError>>()?;
@@ -475,9 +495,12 @@ mod test {
475495
.unwrap()
476496
.into_iter()
477497
.map(|ed| {
478-
ed.strip_prefix(&temp.path().join("course"))
479-
.unwrap()
480-
.to_path_buf()
498+
(
499+
ed.strip_prefix(&temp.path().join("course"))
500+
.unwrap()
501+
.to_path_buf(),
502+
None,
503+
)
481504
})
482505
.collect();
483506
let exercises = get_exercises(
@@ -516,9 +539,12 @@ mod test {
516539
.unwrap()
517540
.into_iter()
518541
.map(|ed| {
519-
ed.strip_prefix(&temp.path().join("clone"))
520-
.unwrap()
521-
.to_path_buf()
542+
(
543+
ed.strip_prefix(&temp.path().join("clone"))
544+
.unwrap()
545+
.to_path_buf(),
546+
None,
547+
)
522548
})
523549
.collect();
524550
let exercises = get_exercises(
@@ -604,10 +630,12 @@ mod test {
604630
init();
605631

606632
let temp = tempfile::tempdir().unwrap();
607-
let exap = temp.path().join("exa");
608-
file_util::create_dir(&exap).unwrap();
609-
let exbp = temp.path().join("exb");
610-
file_util::create_dir(&exbp).unwrap();
633+
let exap = PathBuf::from("exa");
634+
let exap_path = temp.path().join(&exap);
635+
file_util::create_dir(&exap_path).unwrap();
636+
let exbp = PathBuf::from("exb");
637+
let exbp_path = temp.path().join(&exbp);
638+
file_util::create_dir(&exbp_path).unwrap();
611639

612640
let root = TmcProjectYml {
613641
tests_timeout_ms: Some(1234),
@@ -618,21 +646,21 @@ mod test {
618646
tests_timeout_ms: Some(2345),
619647
..Default::default()
620648
};
621-
tpya.save_to_dir(&exap).unwrap();
649+
tpya.save_to_dir(&exap_path).unwrap();
622650
let tpyb = TmcProjectYml {
623651
fail_on_valgrind_error: Some(false),
624652
..Default::default()
625653
};
626-
tpyb.save_to_dir(&exbp).unwrap();
627-
let exercise_dirs = &[exap.clone(), exbp.clone()];
654+
tpyb.save_to_dir(&exbp_path).unwrap();
655+
let exercise_dirs = vec![exap.clone(), exbp.clone()];
628656

629-
merge_tmcproject_configs(root, exercise_dirs).unwrap();
657+
get_and_merge_tmcproject_configs(Some(root), temp.path(), exercise_dirs).unwrap();
630658

631-
let tpya = TmcProjectYml::from(&exap).unwrap();
659+
let tpya = TmcProjectYml::load(&exap_path).unwrap().unwrap();
632660
assert_eq!(tpya.tests_timeout_ms, Some(2345));
633661
assert_eq!(tpya.fail_on_valgrind_error, Some(true));
634662

635-
let tpyb = TmcProjectYml::from(&exbp).unwrap();
663+
let tpyb = TmcProjectYml::load(&exbp_path).unwrap().unwrap();
636664
assert_eq!(tpyb.tests_timeout_ms, Some(1234));
637665
assert_eq!(tpyb.fail_on_valgrind_error, Some(false));
638666
}

tmc-langs/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ pub fn download_or_update_course_exercises(
275275
extract_project(&zip_file, &target.path, false)?;
276276

277277
let plugin = get_language_plugin(&target.path)?;
278-
let tmc_project_yml = TmcProjectYml::from(&target.path)?;
278+
let tmc_project_yml = TmcProjectYml::load_or_default(&target.path)?;
279279
let config =
280280
plugin.get_exercise_packaging_configuration(tmc_project_yml)?;
281281
for student_file in config.student_file_paths {
@@ -748,7 +748,7 @@ pub fn get_exercise_packaging_configuration(
748748
) -> Result<ExercisePackagingConfiguration, LangsError> {
749749
log::debug!("getting exercise packaging config for {}", path.display());
750750

751-
let config = TmcProjectYml::from(path)?;
751+
let config = TmcProjectYml::load_or_default(path)?;
752752
Ok(tmc_langs_plugins::get_language_plugin(path)?
753753
.get_exercise_packaging_configuration(config)?)
754754
}

0 commit comments

Comments
 (0)