Skip to content

Commit c0ecd54

Browse files
committed
Merge branch 'master' of github.com:rage/tmc-langs-rust
2 parents 4cddbdd + d3d5f0a commit c0ecd54

File tree

9 files changed

+276
-54
lines changed

9 files changed

+276
-54
lines changed

plugins/csharp/src/plugin/policy.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! Student file policy for C#
22
3-
use std::ffi::OsString;
43
use std::path::{Path, PathBuf};
54
use tmc_langs_framework::StudentFilePolicy;
65

@@ -17,16 +16,12 @@ impl CSharpStudentFilePolicy {
1716

1817
/// Goes up directories until a bin or obj directory is found
1918
fn is_child_of_binary_dir(&self, path: &Path) -> bool {
20-
let mut parent = path.parent();
21-
while let Some(next) = parent {
22-
if let Some(file_name) = next.file_name() {
23-
if next.is_dir()
24-
&& (file_name == OsString::from("bin") || file_name == OsString::from("obj"))
25-
{
19+
for ancestor in path.ancestors() {
20+
if let Some(file_name) = ancestor.file_name() {
21+
if ancestor.is_dir() && (file_name == "bin" || file_name == "obj") {
2622
return true;
2723
}
2824
}
29-
parent = next.parent();
3025
}
3126
false
3227
}
@@ -35,11 +30,7 @@ impl CSharpStudentFilePolicy {
3530
impl StudentFilePolicy for CSharpStudentFilePolicy {
3631
// false for files in bin or obj directories, true for other files in src
3732
fn is_student_source_file(&self, path: &Path) -> bool {
38-
if self.is_child_of_binary_dir(path) {
39-
false
40-
} else {
41-
path.starts_with("src")
42-
}
33+
path.starts_with("src") && !self.is_child_of_binary_dir(path)
4334
}
4435

4536
fn get_config_file_parent_path(&self) -> &Path {

plugins/python3/src/policy.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ mod test {
5050
assert!(policy.is_student_source_file(Path::new("src/some_file.py")));
5151
}
5252

53+
#[test]
54+
fn in_root_is_source_file() {
55+
let policy = Python3StudentFilePolicy::new(PathBuf::from(""));
56+
assert!(policy.is_student_source_file(Path::new("some_file.py")));
57+
}
58+
59+
#[test]
60+
fn in_subdir_is_source_file() {
61+
let policy = Python3StudentFilePolicy::new(PathBuf::from(""));
62+
assert!(policy.is_student_source_file(Path::new("src/some_dir/some_file.py")));
63+
}
64+
5365
#[test]
5466
fn pycache_is_not_source_file() {
5567
let policy = Python3StudentFilePolicy::new(PathBuf::from(""));

tmc-langs-cli/src/app.rs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -665,19 +665,41 @@ fn create_settings_app() -> App<'static, 'static> {
665665
.takes_value(true),
666666
),
667667
)
668-
.subcommand(
669-
SubCommand::with_name("set")
670-
.about("Saves a value in the settings")
671-
.arg(Arg::with_name("key").help("The key."))
672-
.arg(Arg::with_name("json").help("The value in JSON.")),
673-
)
674668
.subcommand(
675669
SubCommand::with_name("list")
676670
.about("Prints every key=value pair in the settings file."),
677671
)
672+
.subcommand(
673+
SubCommand::with_name("move-projects-dir")
674+
.about(
675+
"Change the projects-dir setting, moving the contents into the new directory",
676+
)
677+
.arg(
678+
Arg::with_name("dir")
679+
.help("The directory where the projects should be moved.")
680+
.required(true)
681+
.takes_value(true),
682+
),
683+
)
678684
.subcommand(
679685
SubCommand::with_name("reset").about("Resets the settings file to the defaults"),
680686
)
687+
.subcommand(
688+
SubCommand::with_name("set")
689+
.about("Saves a value in the settings")
690+
.arg(
691+
Arg::with_name("key")
692+
.help("The key. Parsed as JSON, assumed to be a string if parsing fails.")
693+
.required(true)
694+
.takes_value(true),
695+
)
696+
.arg(
697+
Arg::with_name("json")
698+
.help("The value in JSON.")
699+
.required(true)
700+
.takes_value(true),
701+
),
702+
)
681703
.subcommand(
682704
SubCommand::with_name("unset")
683705
.about("Unsets a value from the settings")

tmc-langs-cli/src/config.rs

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,74 @@
11
//! Handles the CLI's configuration files and credentials.
22
33
use anyhow::{Context, Error};
4+
use serde::{Deserialize, Serialize};
45
use std::env;
56
use std::fs::{self, File};
67
use std::io::Write;
78
use std::path::{Path, PathBuf};
89
use toml::{value::Table, Value};
910

11+
#[derive(Debug, Serialize, Deserialize)]
12+
#[serde(rename_all = "kebab-case")]
13+
pub struct Config {
14+
pub projects_dir: PathBuf,
15+
#[serde(flatten)]
16+
pub table: Table,
17+
}
18+
19+
impl Config {
20+
pub fn get(&self, key: &str) -> ConfigValue {
21+
match key {
22+
"projects-dir" => ConfigValue::Path(&self.projects_dir),
23+
_ => ConfigValue::Value(self.table.get(key)),
24+
}
25+
}
26+
27+
pub fn insert(&mut self, key: String, value: Value) -> Result<(), anyhow::Error> {
28+
match key.as_str() {
29+
"projects-dir" => {
30+
if let Value::String(value) = value {
31+
let path = PathBuf::from(value);
32+
self.set_projects_dir(path)?;
33+
} else {
34+
anyhow::bail!("The value for projects-dir must be a string.")
35+
}
36+
}
37+
_ => {
38+
self.table.insert(key, value);
39+
}
40+
}
41+
Ok(())
42+
}
43+
44+
pub fn remove(&mut self, key: &str) -> Result<Option<Value>, anyhow::Error> {
45+
match key {
46+
"projects-dir" => anyhow::bail!("projects-dir must always be defined"),
47+
_ => Ok(self.table.remove(key)),
48+
}
49+
}
50+
51+
pub fn set_projects_dir(&mut self, mut target: PathBuf) -> Result<PathBuf, anyhow::Error> {
52+
// check if the directory is empty or not
53+
if fs::read_dir(&target)
54+
.with_context(|| format!("Failed to read directory at {}", target.display()))?
55+
.next()
56+
.is_some()
57+
{
58+
anyhow::bail!("Cannot set projects-dir to a non-empty directory.");
59+
}
60+
std::mem::swap(&mut self.projects_dir, &mut target);
61+
Ok(target)
62+
}
63+
}
64+
65+
#[derive(Debug, Serialize)]
66+
#[serde(untagged)]
67+
pub enum ConfigValue<'a> {
68+
Value(Option<&'a Value>),
69+
Path(&'a Path),
70+
}
71+
1072
// base directory for a given plugin's settings files
1173
fn get_tmc_dir(client_name: &str) -> Result<PathBuf, Error> {
1274
let config_dir = match env::var("TMC_LANGS_CONFIG_DIR") {
@@ -35,7 +97,7 @@ fn get_client_stub(client: &str) -> &str {
3597
}
3698

3799
// initializes the default configuration file at the given path
38-
fn init_config_at(client_name: &str, path: &Path) -> Result<Table, Error> {
100+
fn init_config_at(client_name: &str, path: &Path) -> Result<Config, Error> {
39101
let mut file = File::create(&path)
40102
.with_context(|| format!("Failed to create new config file at {}", path.display()))?;
41103

@@ -50,21 +112,20 @@ fn init_config_at(client_name: &str, path: &Path) -> Result<Table, Error> {
50112
)
51113
})?;
52114

53-
let mut config = Table::new();
54-
config.insert(
55-
"projects-folder".to_string(),
56-
Value::String(default_project_dir.to_string_lossy().into_owned()),
57-
);
115+
let config = Config {
116+
projects_dir: default_project_dir,
117+
table: Table::new(),
118+
};
58119

59120
let toml = toml::to_string_pretty(&config).context("Failed to serialize config")?;
60121
file.write_all(toml.as_bytes())
61122
.with_context(|| format!("Failed to write default config to {}", path.display()))?;
62123
Ok(config)
63124
}
64125

65-
pub fn load_config(client_name: &str) -> Result<Table, Error> {
126+
pub fn load_config(client_name: &str) -> Result<Config, Error> {
66127
let path = get_config_path(client_name)?;
67-
match fs::read(&path) {
128+
let config = match fs::read(&path) {
68129
Ok(bytes) => match toml::from_slice(&bytes) {
69130
Ok(config) => Ok(config),
70131
Err(_) => {
@@ -76,10 +137,19 @@ pub fn load_config(client_name: &str) -> Result<Table, Error> {
76137
}
77138
},
78139
Err(_) => init_config_at(client_name, &path),
140+
}?;
141+
if !config.projects_dir.exists() {
142+
fs::create_dir_all(&config.projects_dir).with_context(|| {
143+
format!(
144+
"Failed to create projects-dir at {}",
145+
config.projects_dir.display()
146+
)
147+
})?;
79148
}
149+
Ok(config)
80150
}
81151

82-
pub fn save_config(client_name: &str, config: Table) -> Result<(), Error> {
152+
pub fn save_config(client_name: &str, config: Config) -> Result<(), Error> {
83153
let path = get_config_path(client_name)?;
84154
let toml = toml::to_string_pretty(&config).context("Failed to serialize HashMap")?;
85155
fs::write(&path, toml.as_bytes())

0 commit comments

Comments
 (0)