Skip to content

Commit 5382b8e

Browse files
committed
Merge branch '0.9'
2 parents 5654d99 + 7a6523e commit 5382b8e

File tree

7 files changed

+85
-44
lines changed

7 files changed

+85
-44
lines changed

.github/workflows/clippy.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Clippy check
22

33
on:
44
push:
5-
branches: [master]
5+
branches: [master, '[0-9]+.[0-9]+']
66
pull_request:
7-
branches: [master]
7+
branches: [master, '[0-9]+.[0-9]+']
88

99
jobs:
1010
clippy_check:

.github/workflows/fmt.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Cargo fmt check
22

33
on:
44
push:
5-
branches: [master]
5+
branches: [master, '[0-9]+.[0-9]+']
66
pull_request:
7-
branches: [master]
7+
branches: [master, '[0-9]+.[0-9]+']
88

99
jobs:
1010
cargo_fmt_check:

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Run tests
22

33
on:
44
push:
5-
branches: [master]
5+
branches: [master, '[0-9]+.[0-9]+']
66
pull_request:
7-
branches: [master]
7+
branches: [master, '[0-9]+.[0-9]+']
88

99
env:
1010
RUST_LOG: debug,j4rs=warn

tmc-langs-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "tmc-langs-cli"
3-
version = "0.8.0"
3+
version = "0.9.3"
44
authors = ["University of Helsinki <[email protected]>", "Daniel Martinez <[email protected]>"]
55
edition = "2018"
66
description = "CLI client for TMC"

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

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::Token;
22
use anyhow::{Context, Result};
3+
use file_util::create_file_lock;
34
use serde::{Deserialize, Serialize};
4-
use std::fs::{self, File};
5+
use std::fs;
6+
use std::ops::Deref;
57
use std::path::PathBuf;
68
use tmc_langs_framework::file_util;
79

@@ -29,15 +31,15 @@ impl Credentials {
2931
return Ok(None);
3032
}
3133

32-
file_util::lock!(&credentials_path);
33-
34-
let file = File::open(&credentials_path).with_context(|| {
34+
let mut credentials_file = file_util::open_file_lock(&credentials_path)?;
35+
let guard = credentials_file.lock().with_context(|| {
3536
format!(
36-
"Failed to read credentials file at {}",
37+
"Failed to lock credentials file at {}",
3738
credentials_path.display()
3839
)
3940
})?;
40-
match serde_json::from_reader(file) {
41+
42+
match serde_json::from_reader(guard.deref()) {
4143
Ok(token) => Ok(Some(Credentials {
4244
path: credentials_path,
4345
token,
@@ -64,17 +66,16 @@ impl Credentials {
6466
pub fn save(client_name: &str, token: Token) -> Result<()> {
6567
let credentials_path = Self::get_credentials_path(client_name)?;
6668

67-
file_util::lock!(&credentials_path);
68-
6969
if let Some(p) = credentials_path.parent() {
7070
fs::create_dir_all(p)
7171
.with_context(|| format!("Failed to create directory {}", p.display()))?;
7272
}
73-
let credentials_file = File::create(&credentials_path)
73+
let mut credentials_file = create_file_lock(&credentials_path)
7474
.with_context(|| format!("Failed to create file at {}", credentials_path.display()))?;
75+
let guard = credentials_file.lock()?;
7576

7677
// write token
77-
if let Err(e) = serde_json::to_writer(credentials_file, &token) {
78+
if let Err(e) = serde_json::to_writer(guard.deref(), &token) {
7879
// failed to write token, removing credentials file
7980
fs::remove_file(&credentials_path).with_context(|| {
8081
format!(

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

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
33
use anyhow::{Context, Result};
44
use serde::{Deserialize, Serialize};
5-
use std::io::Write;
5+
use std::io::{Read, Write};
66
use std::path::{Path, PathBuf};
7-
use std::{
8-
borrow::Cow,
9-
fs::{self, File},
10-
};
7+
use std::{borrow::Cow, fs};
118
use tmc_langs_framework::file_util;
129
use toml::{value::Table, Value};
1310

@@ -66,39 +63,59 @@ impl TmcConfig {
6663

6764
pub fn save(self, client_name: &str) -> Result<()> {
6865
let path = Self::get_location(client_name)?;
69-
file_util::lock!(&path);
66+
if let Some(parent) = path.parent() {
67+
file_util::create_dir_all(parent)?;
68+
}
69+
let mut lock = file_util::create_file_lock(&path)?;
70+
let mut guard = lock.lock()?;
7071

7172
let toml = toml::to_string_pretty(&self).context("Failed to serialize HashMap")?;
72-
fs::write(&path, toml.as_bytes())
73+
guard
74+
.write_all(toml.as_bytes())
7375
.with_context(|| format!("Failed to write TOML to {}", path.display()))?;
7476
Ok(())
7577
}
7678

7779
pub fn reset(client_name: &str) -> Result<()> {
7880
let path = Self::get_location(client_name)?;
79-
file_util::lock!(&path);
80-
81-
Self::init_at(client_name, &path)?;
81+
Self::init_at(client_name, &path)?; // init locks the file
8282
Ok(())
8383
}
8484

8585
pub fn load(client_name: &str) -> Result<TmcConfig> {
8686
let path = Self::get_location(client_name)?;
87-
file_util::lock!(&path);
88-
89-
let config = match fs::read(&path) {
90-
Ok(bytes) => match toml::from_slice(&bytes) {
91-
Ok(config) => Ok(config),
92-
Err(_) => {
93-
log::error!(
94-
"Failed to deserialize config at {}, resetting",
95-
path.display()
96-
);
97-
Self::init_at(client_name, &path)
87+
88+
// try to open config file
89+
let config = match file_util::open_file_lock(&path) {
90+
Ok(mut lock) => {
91+
// found config file, lock and read
92+
let mut guard = lock.lock()?;
93+
let mut buf = vec![];
94+
let _bytes = guard.read_to_end(&mut buf)?;
95+
match toml::from_slice(&buf) {
96+
// successfully read file, try to deserialize
97+
Ok(config) => config, // successfully read and deserialized the config
98+
Err(_) => {
99+
log::error!(
100+
"Failed to deserialize config at {}, resetting",
101+
path.display()
102+
);
103+
Self::init_at(client_name, &path)?
104+
}
98105
}
99-
},
100-
Err(_) => Self::init_at(client_name, &path),
101-
}?;
106+
}
107+
Err(e) => {
108+
// failed to open config file, create new one
109+
log::info!(
110+
"could not open config file at {} due to {}, initializing a new config file",
111+
path.display(),
112+
e
113+
);
114+
// todo: check the cause to make sure this makes sense, might be necessary to propagate some error kinds
115+
Self::init_at(client_name, &path)?
116+
}
117+
};
118+
102119
if !config.projects_dir.exists() {
103120
fs::create_dir_all(&config.projects_dir).with_context(|| {
104121
format!(
@@ -112,10 +129,13 @@ impl TmcConfig {
112129

113130
// initializes the default configuration file at the given path
114131
fn init_at(client_name: &str, path: &Path) -> Result<TmcConfig> {
115-
file_util::lock!(path);
132+
if let Some(parent) = path.parent() {
133+
file_util::create_dir_all(parent)?;
134+
}
116135

117-
let mut file = File::create(&path)
136+
let mut lock = file_util::create_file_lock(path)
118137
.with_context(|| format!("Failed to create new config file at {}", path.display()))?;
138+
let mut guard = lock.lock()?;
119139

120140
let default_project_dir = dirs::data_local_dir()
121141
.context("Failed to find local data directory")?
@@ -134,7 +154,8 @@ impl TmcConfig {
134154
};
135155

136156
let toml = toml::to_string_pretty(&config).context("Failed to serialize config")?;
137-
file.write_all(toml.as_bytes())
157+
guard
158+
.write_all(toml.as_bytes())
138159
.with_context(|| format!("Failed to write default config to {}", path.display()))?;
139160
Ok(config)
140161
}

tmc-langs-framework/src/file_util.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Various utility functions, primarily wrapping the standard library's IO and filesystem functions
22
33
use crate::error::FileIo;
4+
use fd_lock::FdLock;
45
use std::fs::{self, File};
56
use std::io::{Read, Write};
67
use std::path::Path;
@@ -25,6 +26,13 @@ pub fn open_file<P: AsRef<Path>>(path: P) -> Result<File, FileIo> {
2526
File::open(path).map_err(|e| FileIo::FileOpen(path.to_path_buf(), e))
2627
}
2728

29+
/// Does not work on directories on Windows.
30+
pub fn open_file_lock<P: AsRef<Path>>(path: P) -> Result<FdLock<File>, FileIo> {
31+
let file = open_file(path)?;
32+
let lock = FdLock::new(file);
33+
Ok(lock)
34+
}
35+
2836
pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, FileIo> {
2937
let path = path.as_ref();
3038
let mut file = open_file(path)?;
@@ -50,6 +58,17 @@ pub fn create_file<P: AsRef<Path>>(path: P) -> Result<File, FileIo> {
5058
File::create(path).map_err(|e| FileIo::FileCreate(path.to_path_buf(), e))
5159
}
5260

61+
pub fn create_file_lock<P: AsRef<Path>>(path: P) -> Result<FdLock<File>, FileIo> {
62+
if let Ok(existing) = open_file(&path) {
63+
// wait for an existing process to be done with the file before rewriting
64+
let mut lock = FdLock::new(existing);
65+
lock.lock().unwrap();
66+
}
67+
let file = create_file(path)?;
68+
let lock = FdLock::new(file);
69+
Ok(lock)
70+
}
71+
5372
pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<(), FileIo> {
5473
let path = path.as_ref();
5574
fs::remove_file(path).map_err(|e| FileIo::FileRemove(path.to_path_buf(), e))

0 commit comments

Comments
 (0)