posemesh_node_registration/
persist.rs

1use anyhow::{anyhow, Result};
2use std::collections::HashMap;
3use std::path::{Path, PathBuf};
4use std::sync::{Mutex, MutexGuard, OnceLock};
5
6pub const NODE_SECRET_PATH: &str = "data/node_secret";
7static NODE_SECRET_STORE: OnceLock<Mutex<HashMap<PathBuf, String>>> = OnceLock::new();
8
9fn node_secret_store() -> &'static Mutex<HashMap<PathBuf, String>> {
10    NODE_SECRET_STORE.get_or_init(|| Mutex::new(HashMap::new()))
11}
12
13fn lock_node_secret_store() -> Result<MutexGuard<'static, HashMap<PathBuf, String>>> {
14    node_secret_store()
15        .lock()
16        .map_err(|_| anyhow!("node secret store poisoned"))
17}
18
19/// Store node secret bytes in memory under the provided `path` identifier.
20pub fn write_node_secret_to_path(path: &Path, secret: &str) -> Result<()> {
21    let mut store = lock_node_secret_store()?;
22    store.insert(path.to_path_buf(), secret.to_owned());
23    Ok(())
24}
25
26/// Read secret contents from `path`. Returns Ok(None) if missing.
27pub fn read_node_secret_from_path(path: &Path) -> Result<Option<String>> {
28    let store = lock_node_secret_store()?;
29    Ok(store.get(path).cloned())
30}
31
32/// Convenience: write to default path `data/node_secret`.
33pub fn write_node_secret(secret: &str) -> Result<()> {
34    write_node_secret_to_path(Path::new(NODE_SECRET_PATH), secret)
35}
36
37/// Convenience: read from default path `data/node_secret`.
38pub fn read_node_secret() -> Result<Option<String>> {
39    read_node_secret_from_path(Path::new(NODE_SECRET_PATH))
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn write_and_read_roundtrip() {
48        let path = PathBuf::from(format!("node_secret_{}", uuid::Uuid::new_v4()));
49
50        write_node_secret_to_path(&path, "first").unwrap();
51        let got = read_node_secret_from_path(&path).unwrap();
52        assert_eq!(got.as_deref(), Some("first"));
53
54        write_node_secret_to_path(&path, "second").unwrap();
55        let got2 = read_node_secret_from_path(&path).unwrap();
56        assert_eq!(got2.as_deref(), Some("second"));
57
58        let other_path = PathBuf::from(format!("node_secret_other_{}", uuid::Uuid::new_v4()));
59        assert!(read_node_secret_from_path(&other_path).unwrap().is_none());
60
61        let mut store = lock_node_secret_store().unwrap();
62        store.remove(&path);
63        store.remove(&other_path);
64    }
65}