Skip to content

Commit 47a868f

Browse files
committed
refactored tmc-langs from anyhow to thiserror
1 parent 8a96a6c commit 47a868f

File tree

11 files changed

+161
-501
lines changed

11 files changed

+161
-501
lines changed

tmc-langs-cli/src/config.rs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ mod tmc_config;
77
pub use self::credentials::Credentials;
88
pub use self::projects_config::{CourseConfig, Exercise, ProjectsConfig};
99
pub use self::tmc_config::{ConfigValue, TmcConfig};
10-
use crate::output::LocalExercise;
1110

1211
use anyhow::{Context, Error};
1312
use std::path::{Path, PathBuf};
@@ -23,29 +22,6 @@ fn get_tmc_dir(client_name: &str) -> Result<PathBuf, Error> {
2322
Ok(config_dir.join(format!("tmc-{}", client_name)))
2423
}
2524

26-
pub fn list_local_course_exercises(
27-
client_name: &str,
28-
course_slug: &str,
29-
) -> Result<Vec<LocalExercise>, anyhow::Error> {
30-
let config_path = TmcConfig::get_location(client_name)?;
31-
let projects_dir = TmcConfig::load(client_name, &config_path)?.projects_dir;
32-
let mut projects_config = ProjectsConfig::load(&projects_dir)?;
33-
34-
let exercises = projects_config
35-
.courses
36-
.remove(course_slug)
37-
.map(|cc| cc.exercises)
38-
.unwrap_or_default();
39-
let mut local_exercises: Vec<LocalExercise> = vec![];
40-
for (exercise_slug, _) in exercises {
41-
local_exercises.push(LocalExercise {
42-
exercise_path: projects_dir.join(course_slug).join(&exercise_slug),
43-
exercise_slug,
44-
})
45-
}
46-
Ok(local_exercises)
47-
}
48-
4925
pub fn migrate(
5026
tmc_config: &TmcConfig,
5127
course_slug: &str,

tmc-langs-util/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub enum FileError {
2525
},
2626
#[error("Failed to create temporary file")]
2727
TempFile(#[source] std::io::Error),
28+
#[error("Failed to read directory at {0}")]
29+
DirRead(PathBuf, #[source] std::io::Error),
2830
#[error("Failed to create directory at {0}")]
2931
DirCreate(PathBuf, #[source] std::io::Error),
3032
#[error("Failed to remove directory at {0}")]

tmc-langs-util/src/file_util.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::error::FileError;
44
use fd_lock::FdLock;
5-
use std::fs::{self, File};
5+
use std::fs::{self, File, ReadDir};
66
use std::io::{Read, Write};
77
use std::path::Path;
88
use walkdir::WalkDir;
@@ -120,6 +120,10 @@ pub fn read_to_file<R: Read, P: AsRef<Path>>(source: &mut R, target: P) -> Resul
120120
Ok(target_file)
121121
}
122122

123+
pub fn read_dir<P: AsRef<Path>>(path: P) -> Result<ReadDir, FileError> {
124+
fs::read_dir(&path).map_err(|e| FileError::DirRead(path.as_ref().to_path_buf(), e))
125+
}
126+
123127
pub fn create_dir<P: AsRef<Path>>(path: P) -> Result<(), FileError> {
124128
fs::create_dir(&path).map_err(|e| FileError::DirCreate(path.as_ref().to_path_buf(), e))
125129
}

tmc-langs/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ tmc-langs-plugins = { path = "../tmc-langs-plugins" }
1111
tmc-langs-framework = { path = "../tmc-langs-framework" }
1212
tmc-langs-util = { path = "../tmc-langs-util" }
1313

14-
anyhow = "1"
1514
dirs = "3"
1615
heim = { version = "0.1.0-beta.3", features = ["disk"] }
1716
impl-enum = "0.2"

tmc-langs/src/config.rs

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,25 @@ mod tmc_config;
77
pub use self::credentials::Credentials;
88
pub use self::projects_config::{CourseConfig, Exercise, ProjectsConfig};
99
pub use self::tmc_config::{ConfigValue, TmcConfig};
10-
use crate::data::LocalExercise;
10+
use crate::{data::LocalExercise, error::LangsError};
1111

12-
use anyhow::{Context, Error};
1312
use std::path::{Path, PathBuf};
14-
use std::{collections::BTreeMap, env, fs};
15-
use tmc_langs_util::file_util;
13+
use std::{collections::BTreeMap, env};
14+
use tmc_langs_util::{file_util, FileError};
1615

1716
// base directory for a given plugin's settings files
18-
fn get_tmc_dir(client_name: &str) -> Result<PathBuf, Error> {
17+
fn get_tmc_dir(client_name: &str) -> Result<PathBuf, LangsError> {
1918
let config_dir = match env::var("TMC_LANGS_CONFIG_DIR") {
2019
Ok(v) => PathBuf::from(v),
21-
Err(_) => dirs::config_dir().context("Failed to find config directory")?,
20+
Err(_) => dirs::config_dir().ok_or(LangsError::NoConfigDir)?,
2221
};
2322
Ok(config_dir.join(format!("tmc-{}", client_name)))
2423
}
2524

2625
pub fn list_local_course_exercises(
2726
client_name: &str,
2827
course_slug: &str,
29-
) -> Result<Vec<LocalExercise>, anyhow::Error> {
28+
) -> Result<Vec<LocalExercise>, LangsError> {
3029
let config_path = TmcConfig::get_location(client_name)?;
3130
let projects_dir = TmcConfig::load(client_name, &config_path)?.projects_dir;
3231
let mut projects_config = ProjectsConfig::load(&projects_dir)?;
@@ -53,7 +52,7 @@ pub fn migrate(
5352
exercise_id: usize,
5453
exercise_checksum: &str,
5554
exercise_path: &Path,
56-
) -> anyhow::Result<()> {
55+
) -> Result<(), LangsError> {
5756
let mut lock = file_util::FileLock::new(exercise_path.to_path_buf())?;
5857
let guard = lock.lock()?;
5958

@@ -72,10 +71,7 @@ pub fn migrate(
7271
exercise_slug,
7372
);
7473
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-
);
74+
return Err(LangsError::DirectoryExists(target_dir));
7975
}
8076

8177
course_config.exercises.insert(
@@ -95,26 +91,23 @@ pub fn move_projects_dir(
9591
mut tmc_config: TmcConfig,
9692
config_path: &Path,
9793
target: PathBuf,
98-
) -> anyhow::Result<()> {
94+
) -> Result<(), LangsError> {
9995
if target.is_file() {
100-
anyhow::bail!("The target path points to a file.")
96+
return Err(FileError::UnexpectedFile(target).into());
10197
}
10298
if !target.exists() {
103-
fs::create_dir_all(&target)
104-
.with_context(|| format!("Failed to create directory at {}", target.display()))?;
99+
file_util::create_dir_all(&target)?;
105100
}
106101

107102
let target_canon = target
108103
.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-
})?;
104+
.map_err(|e| LangsError::Canonicalize(target.clone(), e))?;
105+
let prev_dir_canon = tmc_config
106+
.projects_dir
107+
.canonicalize()
108+
.map_err(|e| LangsError::Canonicalize(target.clone(), e))?;
116109
if target_canon == prev_dir_canon {
117-
anyhow::bail!("Attempted to move the projects-dir to the directory it's already in.")
110+
return Err(LangsError::MovingProjectsDirToItself);
118111
}
119112

120113
let old_projects_dir = tmc_config.set_projects_dir(target.clone())?;

tmc-langs/src/config/credentials.rs

Lines changed: 19 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
use crate::Token;
2-
use anyhow::{Context, Result};
1+
use crate::{LangsError, Token};
32
use serde::{Deserialize, Serialize};
4-
use std::fs;
53
use std::ops::Deref;
64
use std::path::PathBuf;
7-
use tmc_langs_util::file_util::{self, create_file_lock};
5+
use tmc_langs_util::{file_util, FileError};
86

97
#[derive(Debug, Serialize, Deserialize)]
108
pub struct Credentials {
@@ -14,7 +12,7 @@ pub struct Credentials {
1412

1513
impl Credentials {
1614
// path to the credentials file
17-
fn get_credentials_path(client_name: &str) -> Result<PathBuf> {
15+
fn get_credentials_path(client_name: &str) -> Result<PathBuf, LangsError> {
1816
super::get_tmc_dir(client_name).map(|dir| dir.join("credentials.json"))
1917
}
2018

@@ -24,19 +22,16 @@ impl Credentials {
2422
/// - Err if a credentials file exists but cannot be deserialized.
2523
///
2624
/// On Err, the file is deleted.
27-
pub fn load(client_name: &str) -> Result<Option<Self>> {
25+
pub fn load(client_name: &str) -> Result<Option<Self>, LangsError> {
2826
let credentials_path = Self::get_credentials_path(client_name)?;
2927
if !credentials_path.exists() {
3028
return Ok(None);
3129
}
3230

3331
let mut credentials_file = file_util::open_file_lock(&credentials_path)?;
34-
let guard = credentials_file.lock().with_context(|| {
35-
format!(
36-
"Failed to lock credentials file at {}",
37-
credentials_path.display()
38-
)
39-
})?;
32+
let guard = credentials_file
33+
.lock()
34+
.map_err(|e| FileError::FdLock(credentials_path.clone(), e))?;
4035

4136
match serde_json::from_reader(guard.deref()) {
4237
Ok(token) => Ok(Some(Credentials {
@@ -48,55 +43,36 @@ impl Credentials {
4843
"Failed to deserialize credentials.json due to \"{}\", deleting",
4944
e
5045
);
51-
fs::remove_file(&credentials_path).with_context(|| {
52-
format!(
53-
"Failed to remove malformed credentials.json file {}",
54-
credentials_path.display()
55-
)
56-
})?;
57-
anyhow::bail!(
58-
"Failed to deserialize credentials file at {}; removed the file, please try again.",
59-
credentials_path.display()
60-
)
46+
file_util::remove_file(&credentials_path)?;
47+
Err(LangsError::DeserializeCredentials(credentials_path, e))
6148
}
6249
}
6350
}
6451

65-
pub fn save(client_name: &str, token: Token) -> Result<()> {
52+
pub fn save(client_name: &str, token: Token) -> Result<(), LangsError> {
6653
let credentials_path = Self::get_credentials_path(client_name)?;
6754

6855
if let Some(p) = credentials_path.parent() {
69-
fs::create_dir_all(p)
70-
.with_context(|| format!("Failed to create directory {}", p.display()))?;
56+
file_util::create_dir_all(p)?;
7157
}
72-
let mut credentials_file = create_file_lock(&credentials_path)
73-
.with_context(|| format!("Failed to create file at {}", credentials_path.display()))?;
74-
let guard = credentials_file.lock()?;
58+
let mut credentials_file = file_util::create_file_lock(&credentials_path)?;
59+
let guard = credentials_file
60+
.lock()
61+
.map_err(|e| FileError::FdLock(credentials_path.clone(), e))?;
7562

7663
// write token
7764
if let Err(e) = serde_json::to_writer(guard.deref(), &token) {
7865
// failed to write token, removing credentials file
79-
fs::remove_file(&credentials_path).with_context(|| {
80-
format!(
81-
"Failed to remove empty credentials file after failing to write {}",
82-
credentials_path.display()
83-
)
84-
})?;
85-
Err(e).with_context(|| {
86-
format!(
87-
"Failed to write credentials to {}",
88-
credentials_path.display()
89-
)
90-
})?;
66+
file_util::remove_file(&credentials_path)?;
67+
return Err(LangsError::Json(e));
9168
}
9269
Ok(())
9370
}
9471

95-
pub fn remove(self) -> Result<()> {
72+
pub fn remove(self) -> Result<(), LangsError> {
9673
file_util::lock!(&self.path);
9774

98-
fs::remove_file(&self.path)
99-
.with_context(|| format!("Failed to remove credentials at {}", self.path.display()))?;
75+
file_util::remove_file(&self.path)?;
10076
Ok(())
10177
}
10278

tmc-langs/src/config/projects_config.rs

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use anyhow::{Context, Result};
1+
use crate::LangsError;
22
use serde::{Deserialize, Serialize};
33
use std::collections::BTreeMap;
4-
use std::fs;
54
use std::path::{Path, PathBuf};
6-
use tmc_langs_util::file_util;
5+
use tmc_langs_util::{file_util, FileError};
6+
use walkdir::WalkDir;
77

88
#[derive(Debug)]
99
pub struct ProjectsConfig {
@@ -12,26 +12,20 @@ pub struct ProjectsConfig {
1212
}
1313

1414
impl ProjectsConfig {
15-
pub fn load(projects_dir: &Path) -> Result<ProjectsConfig> {
15+
pub fn load(projects_dir: &Path) -> Result<ProjectsConfig, LangsError> {
1616
file_util::lock!(projects_dir);
1717

1818
let mut course_configs = BTreeMap::new();
19-
for file in fs::read_dir(projects_dir)
20-
.with_context(|| format!("Failed to read directory at {}", projects_dir.display()))?
21-
{
22-
let file =
23-
file.with_context(|| format!("Failed to read file in {}", projects_dir.display()))?;
19+
for file in WalkDir::new(projects_dir) {
20+
let file = file?;
2421
let course_config_path = file.path().join("course_config.toml");
2522
if course_config_path.exists() {
2623
let file_name = file.file_name();
27-
let course_dir_name = file_name.to_str().with_context(|| {
28-
format!(
29-
"Course directory name was not valid utf-8: {}",
30-
file.file_name().to_string_lossy()
31-
)
24+
let course_dir_name = file_name.to_str().ok_or_else(|| {
25+
LangsError::FileError(FileError::NoFileName(file.path().to_path_buf()))
3226
})?;
3327

34-
let bytes = fs::read(course_config_path)?;
28+
let bytes = file_util::read_file(course_config_path)?;
3529
let course_config: CourseConfig = toml::from_slice(&bytes)?;
3630

3731
course_configs.insert(course_dir_name.to_string(), course_config);
@@ -63,7 +57,10 @@ impl ProjectsConfig {
6357
}
6458
}
6559
for deleted_exercise in &deleted_exercises {
66-
course_config.exercises.remove(deleted_exercise).unwrap(); // cannot fail
60+
course_config
61+
.exercises
62+
.remove(deleted_exercise)
63+
.expect("this should never fail");
6764
}
6865
if !deleted_exercises.is_empty() {
6966
// if any exercises were deleted, save the course config
@@ -91,22 +88,16 @@ pub struct CourseConfig {
9188
}
9289

9390
impl CourseConfig {
94-
pub fn save_to_projects_dir(&self, projects_dir: &Path) -> Result<()> {
91+
pub fn save_to_projects_dir(&self, projects_dir: &Path) -> Result<(), LangsError> {
9592
file_util::lock!(projects_dir);
9693

9794
let course_dir = projects_dir.join(&self.course);
9895
if !course_dir.exists() {
99-
fs::create_dir_all(&course_dir).with_context(|| {
100-
format!(
101-
"Failed to create course directory at {}",
102-
course_dir.display()
103-
)
104-
})?;
96+
file_util::create_dir_all(&course_dir)?;
10597
}
10698
let target = course_dir.join("course_config.toml");
10799
let s = toml::to_string_pretty(&self)?;
108-
fs::write(&target, s.as_bytes())
109-
.with_context(|| format!("Failed to write course config to {}", target.display()))?;
100+
file_util::write_to_file(s.as_bytes(), &target)?;
110101
Ok(())
111102
}
112103
}

0 commit comments

Comments
 (0)