enigma_node_registry/
store.rs1use 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}