linera_persistent/
file.rs1use std::{
5 io::{self, BufRead as _, Write as _},
6 path::Path,
7};
8
9use fs4::FileExt;
10use thiserror_context::Context;
11
12use super::{Dirty, Persist};
13
14struct Lock(fs_err::File);
16
17#[derive(Debug, thiserror::Error)]
18enum ErrorInner {
19 #[error("I/O error: {0}")]
20 IoError(#[from] std::io::Error),
21 #[error("JSON error: {0}")]
22 JsonError(#[from] serde_json::Error),
23}
24
25thiserror_context::impl_context!(Error(ErrorInner));
26
27trait CleanupExt {
30 type Ok;
31 type Error;
32
33 fn or_cleanup<E>(self, f: impl FnOnce() -> Result<(), E>) -> Result<Self::Ok, Self::Error>
34 where
35 E: Into<Self::Error>,
36 Result<(), E>: Context<Self::Error, Self::Ok, E>;
37}
38
39impl<T, W> CleanupExt for Result<T, W>
40where
41 W: std::fmt::Display + Send + Sync + 'static,
42{
43 type Ok = T;
44 type Error = W;
45
46 fn or_cleanup<E>(self, cleanup: impl FnOnce() -> Result<(), E>) -> Self
47 where
48 E: Into<W>,
49 Result<(), E>: Context<W, T, E>,
50 {
51 self.or_else(|error| {
52 if let Err(cleanup_error) = cleanup() {
53 Err(cleanup_error).context(error)
54 } else {
55 Err(error)
56 }
57 })
58 }
59}
60
61impl Lock {
62 pub fn new(file: fs_err::File) -> std::io::Result<Self> {
65 file.file().try_lock_exclusive()?;
66 Ok(Lock(file))
67 }
68}
69
70impl Drop for Lock {
71 fn drop(&mut self) {
72 if let Err(error) = FileExt::unlock(self.0.file()) {
73 tracing::warn!("Failed to unlock wallet file: {error}");
74 }
75 }
76}
77
78pub struct File<T> {
83 _lock: Lock,
84 path: std::path::PathBuf,
85 value: T,
86 dirty: Dirty,
87}
88
89impl<T> std::ops::Deref for File<T> {
90 type Target = T;
91 fn deref(&self) -> &T {
92 &self.value
93 }
94}
95
96impl<T> std::ops::DerefMut for File<T> {
97 fn deref_mut(&mut self) -> &mut T {
98 *self.dirty = true;
99 &mut self.value
100 }
101}
102
103fn open_options() -> fs_err::OpenOptions {
108 let mut options = fs_err::OpenOptions::new();
109 #[cfg(target_family = "unix")]
110 fs_err::os::unix::fs::OpenOptionsExt::mode(&mut options, 0o600);
111 options.create(true).read(true).write(true);
112 options
113}
114
115impl<T: serde::Serialize + serde::de::DeserializeOwned> File<T> {
116 pub fn new(path: &Path, value: T) -> Result<Self, Error> {
118 let this = Self {
119 _lock: Lock::new(
120 fs_err::OpenOptions::new()
121 .read(true)
122 .write(true)
123 .create(true)
124 .open(path)?,
125 )
126 .with_context(|| format!("locking path {}", path.display()))?,
127 path: path.into(),
128 value,
129 dirty: Dirty::new(true),
130 };
131 Ok(this)
132 }
133
134 pub fn read(path: &Path) -> Result<Self, Error> {
136 Self::read_or_create(path, || {
137 Err(std::io::Error::new(
138 std::io::ErrorKind::NotFound,
139 format!("file is empty or does not exist: {}", path.display()),
140 )
141 .into())
142 })
143 }
144
145 pub fn read_or_create(
148 path: &Path,
149 value: impl FnOnce() -> Result<T, Error>,
150 ) -> Result<Self, Error> {
151 let lock = Lock::new(open_options().read(true).open(path)?)?;
152 let mut reader = io::BufReader::new(&lock.0);
153 let file_is_empty = reader.fill_buf()?.is_empty();
154
155 Ok(Self {
156 value: if file_is_empty {
157 value()?
158 } else {
159 serde_json::from_reader(reader)?
160 },
161 dirty: Dirty::new(file_is_empty),
162 path: path.into(),
163 _lock: lock,
164 })
165 }
166
167 fn save(&mut self) -> Result<(), Error> {
168 let mut temp_file_path = self.path.clone();
169 temp_file_path.set_extension("json.new");
170 let temp_file = open_options().open(&temp_file_path)?;
171 let mut temp_file_writer = std::io::BufWriter::new(temp_file);
172
173 let remove_temp_file = || fs_err::remove_file(&temp_file_path);
174
175 serde_json::to_writer_pretty(&mut temp_file_writer, &self.value)
176 .map_err(Error::from)
177 .or_cleanup(remove_temp_file)?;
178 temp_file_writer
179 .flush()
180 .map_err(Error::from)
181 .or_cleanup(remove_temp_file)?;
182 fs_err::rename(&temp_file_path, &self.path)?;
183 *self.dirty = false;
184 Ok(())
185 }
186}
187
188impl<T: serde::Serialize + serde::de::DeserializeOwned + Send> Persist for File<T> {
189 type Error = Error;
190
191 fn as_mut(&mut self) -> &mut T {
192 &mut self.value
193 }
194
195 async fn persist(&mut self) -> Result<(), Error> {
205 self.save()
206 }
207
208 fn into_value(self) -> T {
210 self.value
211 }
212}