fedimint_portalloc/
data.rs1mod dto;
2
3use std::fs;
4use std::path::PathBuf;
5
6use anyhow::{Result, bail};
7use fs2::FileExt;
8use tracing::{debug, info, warn};
9
10use crate::util;
11
12pub struct DataDir {
14 path: PathBuf,
15 lock_file: fs::File,
16}
17
18impl DataDir {
19 pub fn new(path: impl Into<PathBuf>) -> Result<Self> {
20 let path = path.into();
21 ensure_root_exists(&path)?;
22
23 let lock = util::open_lock_file(&path)?;
24
25 Ok(Self {
26 path,
27 lock_file: lock,
28 })
29 }
30
31 pub fn with_lock<T>(&mut self, f: impl FnOnce(&mut LockedRoot) -> Result<T>) -> Result<T> {
32 f(&mut LockedRoot::new(&self.path, &mut self.lock_file)?)
33 }
34}
35
36fn ensure_root_exists(dir: &PathBuf) -> Result<()> {
37 if !dir.try_exists()? {
38 info!(dir = %dir.display(), "Creating root dir");
39 fs::create_dir_all(dir)?;
40 }
41 Ok(())
42}
43
44pub struct LockedRoot<'a> {
46 path: &'a PathBuf,
47 lock_file: &'a mut fs::File,
48 locked: bool,
49}
50
51impl<'a> Drop for LockedRoot<'a> {
52 fn drop(&mut self) {
53 if self.locked {
54 let Ok(()) = FileExt::unlock(self.lock_file) else {
55 warn!("Failed to release the lock file");
56 return;
57 };
58 self.locked = false;
59 }
60 }
61}
62
63impl<'a> LockedRoot<'a> {
64 fn new(path: &'a PathBuf, lock_file: &'a mut fs::File) -> Result<Self> {
65 let mut locked_root = Self {
66 path,
67 lock_file,
68 locked: false,
69 };
70 locked_root.lock()?;
71 Ok(locked_root)
72 }
73
74 fn lock(&mut self) -> Result<()> {
75 debug!(path = %self.path.display(), "Acquiring lock...");
76 if self.lock_file.try_lock_exclusive().is_err() {
77 info!("Lock taken, waiting...");
78 self.lock_file.lock_exclusive()?;
79 info!("Acquired lock after wait");
80 };
81 debug!("Acquired lock");
82 self.locked = true;
83 Ok(())
84 }
85
86 fn data_file_path(&self) -> PathBuf {
87 self.path.join("fm-portalloc.json")
88 }
89
90 fn ensure_locked(&self) -> anyhow::Result<()> {
91 if !self.locked {
92 bail!("LockedRoot no longer valid");
93 }
94 Ok(())
95 }
96
97 pub fn load_data(&self) -> Result<dto::RootData> {
98 self.ensure_locked()?;
99 let path = self.data_file_path();
100 if !path.try_exists()? {
101 return Ok(Default::default());
102 }
103 Ok(serde_json::from_reader::<_, _>(std::fs::File::open(path)?)?)
104 }
105
106 pub fn store_data(&mut self, data: &dto::RootData) -> Result<()> {
107 util::store_json_pretty_to_file(&self.data_file_path(), data)
108 }
109}