disk.rs
2.0 kB · rust · 66 lines
1use crate::model::Result;2use std::cell::RefCell;3use std::io::Write;4use std::path::{Path, PathBuf};5use std::process::{Command, Stdio};67const EDITION: &str = "2021";89thread_local! {10 static STALE: RefCell<Option<Vec<PathBuf>>> = const { RefCell::new(None) };11}1213pub fn watch() {14 STALE.with(|stale| *stale.borrow_mut() = Some(Vec::new()));15}1617pub fn stale() -> Vec<PathBuf> {18 STALE.with(|stale| stale.borrow_mut().take().unwrap_or_default())19}2021pub fn save(path: &Path, text: &str) -> Result<()> {22 let at = |e: String| format!("{}: {e}", path.display());23 let text = if path.extension().is_some_and(|e| e == "rs") {24 rustfmt(text).map_err(at)?25 } else {26 text.to_string()27 };28 if std::fs::read_to_string(path).ok().as_deref() == Some(text.as_str()) {29 return Ok(());30 }31 let watched = STALE.with(|stale| {32 let mut stale = stale.borrow_mut();33 if let Some(list) = stale.as_mut() {34 list.push(path.to_path_buf());35 }36 stale.is_some()37 });38 if watched {39 return Ok(());40 }41 if let Some(dir) = path.parent() {42 std::fs::create_dir_all(dir).map_err(|e| at(e.to_string()))?;43 }44 std::fs::write(path, text).map_err(|e| at(e.to_string()))45}4647pub fn rustfmt(text: &str) -> Result<String> {48 let mut child = Command::new("rustfmt")49 .args(["--edition", EDITION])50 .stdin(Stdio::piped())51 .stdout(Stdio::piped())52 .stderr(Stdio::piped())53 .spawn()54 .map_err(|e| format!("rustfmt: {e}"))?;55 let mut stdin = child.stdin.take().ok_or("rustfmt: no stdin")?;56 let out = std::thread::scope(|scope| {57 scope.spawn(move || stdin.write_all(text.as_bytes()));58 child.wait_with_output()59 })60 .map_err(|e| format!("rustfmt: {e}"))?;61 if !out.status.success() {62 let said = String::from_utf8_lossy(&out.stderr);63 return Err(format!("rustfmt: {}", said.trim()));64 }65 String::from_utf8(out.stdout).map_err(|e| format!("rustfmt: {e}"))66}