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::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}
87
88impl<T> std::ops::Deref for File<T> {
89 type Target = T;
90 fn deref(&self) -> &T {
91 &self.value
92 }
93}
94
95impl<T> std::ops::DerefMut for File<T> {
96 fn deref_mut(&mut self) -> &mut T {
97 &mut self.value
98 }
99}
100
101fn open_options() -> fs_err::OpenOptions {
106 let mut options = fs_err::OpenOptions::new();
107 #[cfg(target_family = "unix")]
108 fs_err::os::unix::fs::OpenOptionsExt::mode(&mut options, 0o600);
109 options.create(true).read(true).write(true);
110 options
111}
112
113impl<T: serde::Serialize + serde::de::DeserializeOwned> File<T> {
114 pub fn new(path: &Path, value: T) -> Result<Self, Error> {
116 let this = Self {
117 _lock: Lock::new(
118 fs_err::OpenOptions::new()
119 .read(true)
120 .write(true)
121 .create(true)
122 .open(path)?,
123 )
124 .with_context(|| format!("locking path {}", path.display()))?,
125 path: path.into(),
126 value,
127 };
128 this.save()?;
129 Ok(this)
130 }
131
132 pub fn read(path: &Path) -> Result<Self, Error> {
134 Self::read_or_create(path, || {
135 Err(std::io::Error::new(
136 std::io::ErrorKind::NotFound,
137 format!("file is empty or does not exist: {}", path.display()),
138 )
139 .into())
140 })
141 }
142
143 pub fn read_or_create(
146 path: &Path,
147 value: impl FnOnce() -> Result<T, Error>,
148 ) -> Result<Self, Error> {
149 let lock = Lock::new(open_options().read(true).open(path)?)?;
150 let mut reader = io::BufReader::new(&lock.0);
151 let file_is_empty = reader.fill_buf()?.is_empty();
152
153 let me = Self {
154 value: if file_is_empty {
155 value()?
156 } else {
157 serde_json::from_reader(reader)?
158 },
159 path: path.into(),
160 _lock: lock,
161 };
162
163 me.save()?;
164
165 Ok(me)
166 }
167
168 pub fn save(&self) -> Result<(), Error> {
169 let mut temp_file_path = self.path.clone();
170 temp_file_path.set_extension("json.new");
171 let temp_file = open_options().open(&temp_file_path)?;
172 let mut temp_file_writer = std::io::BufWriter::new(temp_file);
173
174 let remove_temp_file = || fs_err::remove_file(&temp_file_path);
175
176 serde_json::to_writer_pretty(&mut temp_file_writer, &self.value)
177 .map_err(Error::from)
178 .or_cleanup(remove_temp_file)?;
179 temp_file_writer
180 .flush()
181 .map_err(Error::from)
182 .or_cleanup(remove_temp_file)?;
183 drop(temp_file_writer);
184 fs_err::rename(&temp_file_path, &self.path)?;
185 Ok(())
186 }
187}
188
189impl<T: serde::Serialize + serde::de::DeserializeOwned + Send> Persist for File<T> {
190 type Error = Error;
191
192 fn as_mut(&mut self) -> &mut T {
193 &mut self.value
194 }
195
196 fn persist(&mut self) -> impl std::future::Future<Output = Result<(), Error>> {
206 let result = self.save();
207 async { result }
208 }
209
210 fn into_value(self) -> T {
212 self.value
213 }
214}