Skip to main content

interstice_core/persistence/
peer_tokens.rs

1use crate::error::IntersticeError;
2use crate::node::NodeId;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::path::{Path, PathBuf};
6use uuid::Uuid;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9struct PeerTokenFile {
10    local_token: String,
11    peers: HashMap<String, String>,
12}
13
14pub struct PeerTokenStore {
15    path: Option<PathBuf>,
16    local_token: String,
17    peers: HashMap<String, String>,
18}
19
20impl PeerTokenStore {
21    pub fn load_or_create<P: AsRef<Path>>(path: P) -> Result<Self, IntersticeError> {
22        let path = path.as_ref().to_path_buf();
23        if path.exists() {
24            let contents = std::fs::read_to_string(&path).map_err(|err| {
25                IntersticeError::Internal(format!(
26                    "Failed to read peer token store {}: {err}",
27                    path.display()
28                ))
29            })?;
30            let file: PeerTokenFile = if contents.trim().is_empty() {
31                PeerTokenFile {
32                    local_token: Self::generate_token(),
33                    peers: HashMap::new(),
34                }
35            } else {
36                toml::from_str(&contents).map_err(|err| {
37                    IntersticeError::Internal(format!(
38                        "Failed to parse peer token store {}: {err}",
39                        path.display()
40                    ))
41                })?
42            };
43            let loaded_local_token = file.local_token.clone();
44            let mut store = Self {
45                path: Some(path),
46                local_token: file.local_token,
47                peers: file.peers,
48            };
49            if store.local_token.trim().is_empty() {
50                store.local_token = Self::generate_token();
51            }
52            if contents.trim().is_empty() || store.local_token != loaded_local_token {
53                store.save()?;
54            }
55            Ok(store)
56        } else {
57            let store = Self {
58                path: Some(path),
59                local_token: Self::generate_token(),
60                peers: HashMap::new(),
61            };
62            store.save()?;
63            Ok(store)
64        }
65    }
66
67    pub fn new_in_memory() -> Self {
68        Self {
69            path: None,
70            local_token: Self::generate_token(),
71            peers: HashMap::new(),
72        }
73    }
74
75    pub fn local_token(&self) -> String {
76        self.local_token.clone()
77    }
78
79    pub fn get_peer_token(&self, peer_id: &NodeId) -> Option<String> {
80        self.peers.get(&peer_id.to_string()).cloned()
81    }
82
83    pub fn set_peer_token(
84        &mut self,
85        peer_id: &NodeId,
86        token: String,
87    ) -> Result<(), IntersticeError> {
88        self.peers.insert(peer_id.to_string(), token);
89        self.save()
90    }
91
92    pub fn save(&self) -> Result<(), IntersticeError> {
93        let Some(path) = &self.path else {
94            return Ok(());
95        };
96        let file = PeerTokenFile {
97            local_token: self.local_token.clone(),
98            peers: self.peers.clone(),
99        };
100        let contents = toml::to_string_pretty(&file).map_err(|err| {
101            IntersticeError::Internal(format!("Failed to serialize peer token store: {err}"))
102        })?;
103        std::fs::write(path, contents).map_err(|err| {
104            IntersticeError::Internal(format!(
105                "Failed to write peer token store {}: {err}",
106                path.display()
107            ))
108        })?;
109        Ok(())
110    }
111
112    fn generate_token() -> String {
113        Uuid::new_v4().to_string()
114    }
115}