1use std::fs;
2use std::path::{Path, PathBuf};
3
4use anyhow::{Context, Result};
5use serde::Serialize;
6
7pub fn ensure_dirs(dirs: &[PathBuf]) -> Result<()> {
9 for dir in dirs {
10 fs::create_dir_all(dir).with_context(|| format!("failed to create {}", dir.display()))?;
11 }
12 Ok(())
13}
14
15pub fn write_json_file<T: Serialize>(path: &Path, value: &T) -> Result<()> {
17 if let Some(parent) = path.parent() {
18 fs::create_dir_all(parent).with_context(|| format!("failed to create {}", parent.display()))?;
19 }
20
21 let mut payload = serde_json::to_string_pretty(value).context("failed to serialize json")?;
22 payload.push('\n');
23 write_private_file(path, payload.as_bytes())
24}
25
26pub fn write_private_file(path: &Path, contents: &[u8]) -> Result<()> {
28 #[cfg(unix)]
29 {
30 use std::fs::OpenOptions;
31 use std::io::Write;
32 use std::os::unix::fs::OpenOptionsExt;
33
34 let mut file = OpenOptions::new()
35 .write(true)
36 .create_new(true)
37 .mode(0o600)
38 .open(path)
39 .with_context(|| format!("failed to write {}", path.display()))?;
40 file.write_all(contents)?;
41 return Ok(());
42 }
43
44 #[cfg(not(unix))]
45 {
46 fs::write(path, contents).with_context(|| format!("failed to write {}", path.display()))?;
47 Ok(())
48 }
49}
50
51pub fn write_file_if_missing(path: &Path, contents: &[u8]) -> Result<()> {
53 if path.exists() {
54 return Ok(());
55 }
56
57 if let Some(parent) = path.parent() {
58 fs::create_dir_all(parent).with_context(|| format!("failed to create {}", parent.display()))?;
59 }
60
61 fs::write(path, contents).with_context(|| format!("failed to write {}", path.display()))?;
62 Ok(())
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn write_file_if_missing_is_idempotent() {
71 let tmp = tempfile::tempdir().expect("tempdir");
72 let path = tmp.path().join("marker.txt");
73
74 write_file_if_missing(&path, b"first").expect("first write");
75 write_file_if_missing(&path, b"second").expect("second write");
76
77 assert_eq!(fs::read_to_string(path).expect("read marker"), "first");
78 }
79}