pub mod tree;
pub mod watcher;
use std::fs::{self, OpenOptions};
use std::path::Path;
use anyhow::{Context, Result};
pub fn read_file(path: &Path) -> Result<String> {
let bytes = fs::read(path).with_context(|| format!("cannot read {}", path.display()))?;
String::from_utf8(bytes).with_context(|| format!("{} is not valid UTF-8", path.display()))
}
pub fn create_file(path: &Path) -> Result<()> {
OpenOptions::new()
.write(true)
.create_new(true)
.open(path)
.with_context(|| format!("cannot create {}", path.display()))?;
Ok(())
}
pub fn create_directory(path: &Path) -> Result<()> {
fs::create_dir(path).with_context(|| format!("cannot create {}", path.display()))
}
pub fn write_file(path: &Path, contents: &str) -> Result<()> {
let temp = temp_path(path);
fs::write(&temp, contents).with_context(|| format!("cannot write {}", temp.display()))?;
fs::rename(&temp, path)
.with_context(|| format!("cannot replace {}", path.display()))
.inspect_err(|_| {
let _ = fs::remove_file(&temp);
})
}
fn temp_path(path: &Path) -> std::path::PathBuf {
let mut name = std::ffi::OsString::from(".");
name.push(path.file_name().unwrap_or_else(|| "termi".as_ref()));
name.push(".termi-tmp");
path.with_file_name(name)
}
#[cfg(test)]
mod tests {
use super::*;
fn fixture(name: &str) -> std::path::PathBuf {
let root = std::env::temp_dir().join(format!("termi-fs-{name}"));
let _ = fs::remove_dir_all(&root);
fs::create_dir(&root).expect("create fixture");
root
}
#[test]
fn creates_new_files_without_overwriting() {
let root = fixture("create-file");
let path = root.join("notes.txt");
create_file(&path).expect("create file");
fs::write(&path, "keep me").expect("write fixture");
assert!(create_file(&path).is_err());
assert_eq!(fs::read_to_string(path).expect("read file"), "keep me");
}
#[test]
fn creates_one_directory() {
let root = fixture("create-directory");
let path = root.join("drafts");
create_directory(&path).expect("create directory");
assert!(path.is_dir());
assert!(create_directory(&path).is_err());
}
}