Skip to content

Commit 2b53885

Browse files
authored
Merge pull request #104 from rage/fix-lock
Fix lock
2 parents 6cb5bfa + 014e205 commit 2b53885

File tree

5 files changed

+220
-75
lines changed

5 files changed

+220
-75
lines changed

tmc-langs-cli/src/config.rs

Lines changed: 187 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ pub use self::tmc_config::{ConfigValue, TmcConfig};
1010
use crate::output::LocalExercise;
1111

1212
use anyhow::{Context, Error};
13-
use std::env;
14-
use std::path::PathBuf;
13+
use std::path::{Path, PathBuf};
14+
use std::{collections::BTreeMap, env, fs};
15+
use tmc_langs_framework::file_util;
1516

1617
// base directory for a given plugin's settings files
1718
fn get_tmc_dir(client_name: &str) -> Result<PathBuf, Error> {
@@ -26,7 +27,8 @@ pub fn list_local_course_exercises(
2627
client_name: &str,
2728
course_slug: &str,
2829
) -> Result<Vec<LocalExercise>, anyhow::Error> {
29-
let projects_dir = TmcConfig::load(client_name)?.projects_dir;
30+
let config_path = TmcConfig::get_location(client_name)?;
31+
let projects_dir = TmcConfig::load(client_name, &config_path)?.projects_dir;
3032
let mut projects_config = ProjectsConfig::load(&projects_dir)?;
3133

3234
let exercises = projects_config
@@ -43,3 +45,185 @@ pub fn list_local_course_exercises(
4345
}
4446
Ok(local_exercises)
4547
}
48+
49+
pub fn migrate(
50+
tmc_config: &TmcConfig,
51+
course_slug: &str,
52+
exercise_slug: &str,
53+
exercise_id: usize,
54+
exercise_checksum: &str,
55+
exercise_path: &Path,
56+
) -> anyhow::Result<()> {
57+
let mut lock = file_util::FileLock::new(exercise_path.to_path_buf())?;
58+
let guard = lock.lock()?;
59+
60+
let mut projects_config = ProjectsConfig::load(&tmc_config.projects_dir)?;
61+
let course_config = projects_config
62+
.courses
63+
.entry(course_slug.to_string())
64+
.or_insert(CourseConfig {
65+
course: course_slug.to_string(),
66+
exercises: BTreeMap::new(),
67+
});
68+
69+
let target_dir = ProjectsConfig::get_exercise_download_target(
70+
&tmc_config.projects_dir,
71+
course_slug,
72+
exercise_slug,
73+
);
74+
if target_dir.exists() {
75+
anyhow::bail!(
76+
"Tried to migrate exercise to {}; however, something already exists at that path.",
77+
target_dir.display()
78+
);
79+
}
80+
81+
course_config.exercises.insert(
82+
exercise_slug.to_string(),
83+
Exercise {
84+
id: exercise_id,
85+
checksum: exercise_checksum.to_string(),
86+
},
87+
);
88+
89+
super::move_dir(exercise_path, guard, &target_dir)?;
90+
course_config.save_to_projects_dir(&tmc_config.projects_dir)?;
91+
Ok(())
92+
}
93+
94+
pub fn move_projects_dir(
95+
mut tmc_config: TmcConfig,
96+
config_path: &Path,
97+
target: PathBuf,
98+
) -> anyhow::Result<()> {
99+
if target.is_file() {
100+
anyhow::bail!("The target path points to a file.")
101+
}
102+
if !target.exists() {
103+
fs::create_dir_all(&target)
104+
.with_context(|| format!("Failed to create directory at {}", target.display()))?;
105+
}
106+
107+
let target_canon = target
108+
.canonicalize()
109+
.with_context(|| format!("Failed to canonicalize {}", target.display()))?;
110+
let prev_dir_canon = tmc_config.projects_dir.canonicalize().with_context(|| {
111+
format!(
112+
"Failed to canonicalize {}",
113+
tmc_config.projects_dir.display()
114+
)
115+
})?;
116+
if target_canon == prev_dir_canon {
117+
anyhow::bail!("Attempted to move the projects-dir to the directory it's already in.")
118+
}
119+
120+
let old_projects_dir = tmc_config.set_projects_dir(target.clone())?;
121+
122+
let mut lock = file_util::FileLock::new(old_projects_dir.clone())?;
123+
let guard = lock.lock()?;
124+
125+
super::move_dir(&old_projects_dir, guard, &target)?;
126+
tmc_config.save(config_path)?;
127+
Ok(())
128+
}
129+
130+
#[cfg(test)]
131+
mod test {
132+
use toml::value::Table;
133+
134+
use super::*;
135+
136+
fn init() {
137+
use log::*;
138+
use simple_logger::*;
139+
let _ = SimpleLogger::new().with_level(LevelFilter::Debug).init();
140+
}
141+
142+
fn file_to(
143+
target_dir: impl AsRef<std::path::Path>,
144+
target_relative: impl AsRef<std::path::Path>,
145+
contents: impl AsRef<[u8]>,
146+
) -> PathBuf {
147+
let target = target_dir.as_ref().join(target_relative);
148+
if let Some(parent) = target.parent() {
149+
std::fs::create_dir_all(parent).unwrap();
150+
}
151+
std::fs::write(&target, contents.as_ref()).unwrap();
152+
target
153+
}
154+
155+
#[test]
156+
fn migrates() {
157+
init();
158+
159+
let projects_dir = tempfile::tempdir().unwrap();
160+
let exercise_path = tempfile::tempdir().unwrap();
161+
162+
let tmc_config = TmcConfig {
163+
projects_dir: projects_dir.path().to_path_buf(),
164+
table: Table::new(),
165+
};
166+
167+
file_to(&exercise_path, "some_file", "");
168+
169+
assert!(!projects_dir
170+
.path()
171+
.join("course/exercise/some_file")
172+
.exists());
173+
174+
migrate(
175+
&tmc_config,
176+
"course",
177+
"exercise",
178+
0,
179+
"checksum",
180+
exercise_path.path(),
181+
)
182+
.unwrap();
183+
184+
assert!(projects_dir
185+
.path()
186+
.join("course/exercise/some_file")
187+
.exists());
188+
189+
assert!(!exercise_path.path().exists());
190+
}
191+
192+
#[test]
193+
fn moves_projects_dir() {
194+
init();
195+
196+
let projects_dir = tempfile::tempdir().unwrap();
197+
let target_dir = tempfile::tempdir().unwrap();
198+
199+
let config_path = tempfile::NamedTempFile::new().unwrap();
200+
let tmc_config = TmcConfig {
201+
projects_dir: projects_dir.path().to_path_buf(),
202+
table: Table::new(),
203+
};
204+
205+
file_to(
206+
projects_dir.path(),
207+
"some course/some exercise/some file",
208+
"",
209+
);
210+
211+
assert!(!target_dir
212+
.path()
213+
.join("some course/some exercise/some file")
214+
.exists());
215+
216+
move_projects_dir(
217+
tmc_config,
218+
config_path.path(),
219+
target_dir.path().to_path_buf(),
220+
)
221+
.unwrap();
222+
223+
assert!(target_dir
224+
.path()
225+
.join("some course/some exercise/some file")
226+
.exists());
227+
assert!(!projects_dir.path().exists());
228+
}
229+
}

tmc-langs-cli/src/config/tmc_config.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ impl TmcConfig {
6161
Ok(target)
6262
}
6363

64-
pub fn save(self, client_name: &str) -> Result<()> {
65-
let path = Self::get_location(client_name)?;
64+
pub fn save(self, path: &Path) -> Result<()> {
6665
if let Some(parent) = path.parent() {
6766
file_util::create_dir_all(parent)?;
6867
}
@@ -82,9 +81,7 @@ impl TmcConfig {
8281
Ok(())
8382
}
8483

85-
pub fn load(client_name: &str) -> Result<TmcConfig> {
86-
let path = Self::get_location(client_name)?;
87-
84+
pub fn load(client_name: &str, path: &Path) -> Result<TmcConfig> {
8885
// try to open config file
8986
let config = match file_util::open_file_lock(&path) {
9087
Ok(mut lock) => {
@@ -161,11 +158,11 @@ impl TmcConfig {
161158
}
162159

163160
// path to the configuration file
164-
fn get_location(client_name: &str) -> Result<PathBuf> {
161+
pub fn get_location(client_name: &str) -> Result<PathBuf> {
165162
super::get_tmc_dir(client_name).map(|dir| dir.join("config.toml"))
166163
}
167164

168-
// some client use a different name for the directory
165+
// some clients use a different name for the directory
169166
fn get_client_stub(client: &str) -> &str {
170167
match client {
171168
"vscode_plugin" => "vscode",

0 commit comments

Comments
 (0)