1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
//! Helper module for loading and storing via serde
//!
//! Utilities to load or store a serde-able object,
//! in JSON format,
//! to/from a disk file at a caller-specified filename.
//!
//! The caller is supposed to do any necessary locking.
//!
//! The entrypoints are methods on `[Target]`,
//! which the caller is supposed to construct.
use std::path::Path;
use fs_mistrust::CheckedDir;
use serde::{Serialize, de::DeserializeOwned};
use tor_basic_utils::PathExt;
use tor_error::ErrorReport as _;
use tracing::trace;
use crate::err::ErrorSource;
/// Common arguments to load/store operations
#[derive(derive_more::Display)]
#[display("{:?}", dir.as_path().join(rel_fname).as_path().display_lossy())]
pub(crate) struct Target<'r> {
/// Directory
pub(crate) dir: &'r CheckedDir,
/// Filename relative to `dir`
///
/// Might be a leafname; must be relative
/// Should include the `.json` extension.
pub(crate) rel_fname: &'r Path,
}
impl Target<'_> {
/// Load and deserialize a `D` from the file specified by `self`
///
/// Returns `None` if the file doesn't exist.
pub(crate) fn load<D: DeserializeOwned>(&self) -> Result<Option<D>, ErrorSource> {
let string = match self.dir.read_to_string(self.rel_fname) {
Ok(string) => string,
Err(fs_mistrust::Error::NotFound(_)) => {
trace!("loading {self} (not found)");
return Ok(None);
}
Err(e) => {
trace!("loading {self}, error {}", e.report());
return Err(e.into());
}
};
let r = serde_json::from_str(&string)?;
trace!("loaded {self}");
Ok(Some(r))
}
/// Serialise and store an `S` to the file specified by `self`
///
/// Concurrent readers (using `load`) will see either the old data,
/// or the new data,
/// not corruption or a mixture.
///
/// Likewise, if something fails, the old data will remain.
/// (But, we do *not* use `fsync`.)
///
/// It is a serious bug to make several concurrent calls to `store`
/// for the same file.
/// That might result in corrupted files.
///
/// See [`fs_mistrust::CheckedDir::write_and_replace`]
/// for more details about the semantics.
pub(crate) fn store<S: Serialize>(&self, val: &S) -> Result<(), ErrorSource> {
trace!("storing {self}");
let output = serde_json::to_string_pretty(val)?;
self.dir.write_and_replace(self.rel_fname, output)?;
Ok(())
}
/// Delete the file specified by `self`
pub(crate) fn delete(&self) -> Result<(), ErrorSource> {
trace!("deleting {self}");
self.dir.remove_file(self.rel_fname)?;
Ok(())
}
}
|