enigma_node_registry/
store.rs

1use std::collections::{HashMap, HashSet};
2
3use enigma_node_types::{NodeInfo, Presence, PublicIdentity, UserId};
4use tokio::sync::RwLock;
5
6use crate::error::{EnigmaNodeRegistryError, Result};
7
8pub struct Store {
9    inner: RwLock<StoreState>,
10    max_nodes: usize,
11}
12
13struct StoreState {
14    identities: HashMap<UserId, PublicIdentity>,
15    presences: HashMap<UserId, Presence>,
16    nodes: Vec<NodeInfo>,
17}
18
19impl Store {
20    pub fn new(max_nodes: usize) -> Self {
21        let capped = max_nodes.max(1);
22        Store {
23            inner: RwLock::new(StoreState {
24                identities: HashMap::new(),
25                presences: HashMap::new(),
26                nodes: Vec::new(),
27            }),
28            max_nodes: capped,
29        }
30    }
31
32    pub async fn register(&self, identity: PublicIdentity) -> Result<()> {
33        identity.validate()?;
34        let mut guard = self.inner.write().await;
35        if guard.identities.contains_key(&identity.user_id) {
36            return Err(EnigmaNodeRegistryError::Conflict);
37        }
38        guard.identities.insert(identity.user_id, identity);
39        Ok(())
40    }
41
42    pub async fn resolve(&self, user_id: &UserId) -> Result<Option<PublicIdentity>> {
43        let guard = self.inner.read().await;
44        Ok(guard.identities.get(user_id).cloned())
45    }
46
47    pub async fn check_user(&self, user_id: &UserId) -> Result<bool> {
48        let guard = self.inner.read().await;
49        Ok(guard.identities.contains_key(user_id))
50    }
51
52    pub async fn announce(&self, presence: Presence) -> Result<()> {
53        presence.validate()?;
54        let mut guard = self.inner.write().await;
55        guard.presences.insert(presence.user_id, presence);
56        Ok(())
57    }
58
59    pub async fn sync_identities(&self, identities: Vec<PublicIdentity>) -> Result<usize> {
60        let mut valid = Vec::new();
61        for identity in identities {
62            identity.validate()?;
63            valid.push(identity);
64        }
65        let mut guard = self.inner.write().await;
66        let mut inserted = 0usize;
67        for identity in valid {
68            if guard.identities.contains_key(&identity.user_id) {
69                continue;
70            }
71            guard.identities.insert(identity.user_id, identity);
72            inserted = inserted.saturating_add(1);
73        }
74        Ok(inserted)
75    }
76
77    pub async fn list_nodes(&self) -> Result<Vec<NodeInfo>> {
78        let guard = self.inner.read().await;
79        Ok(guard.nodes.clone())
80    }
81
82    pub async fn add_nodes(&self, nodes: Vec<NodeInfo>) -> Result<usize> {
83        let mut validated = Vec::new();
84        for node in nodes {
85            node.validate()?;
86            let base = node.base_url.trim().to_string();
87            validated.push(NodeInfo { base_url: base });
88        }
89        let mut guard = self.inner.write().await;
90        let mut existing: HashSet<String> =
91            guard.nodes.iter().map(|n| n.base_url.clone()).collect();
92        let mut inserted = 0usize;
93        for node in validated {
94            if guard.nodes.len() >= self.max_nodes {
95                break;
96            }
97            if existing.contains(&node.base_url) {
98                continue;
99            }
100            guard.nodes.push(node.clone());
101            existing.insert(node.base_url.clone());
102            inserted = inserted.saturating_add(1);
103            if guard.nodes.len() >= self.max_nodes {
104                break;
105            }
106        }
107        Ok(inserted)
108    }
109
110    pub async fn purge_presences(&self, now_ms: u64, ttl_secs: u64) -> usize {
111        let ttl_ms = ttl_secs.saturating_mul(1000);
112        let mut guard = self.inner.write().await;
113        let before = guard.presences.len();
114        guard.presences.retain(|_, presence| {
115            let age = now_ms.saturating_sub(presence.ts_ms);
116            age < ttl_ms
117        });
118        before.saturating_sub(guard.presences.len())
119    }
120
121    #[cfg(test)]
122    pub async fn presence_exists(&self, user_id: &UserId) -> bool {
123        let guard = self.inner.read().await;
124        guard.presences.contains_key(user_id)
125    }
126}