1use gfeh_core::NodeRef;
12use std::collections::HashMap;
13use std::sync::{Arc, RwLock};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Exposed {
18 pub node: NodeRef,
20 pub filename: Option<String>,
22 pub enabled: bool,
28}
29
30pub trait Exposures: Send + Sync + 'static {
32 fn resolve(&self, token: &str) -> Option<Exposed>;
34}
35
36#[derive(Debug, Default, Clone)]
41pub struct StaticExposures {
42 entries: Arc<RwLock<HashMap<String, Exposed>>>,
43}
44
45impl StaticExposures {
46 #[must_use]
48 pub fn new() -> Self {
49 Self::default()
50 }
51
52 #[must_use]
54 pub fn with(self, token: &str, exposed: Exposed) -> Self {
55 self.publish(token, exposed);
56 self
57 }
58
59 pub fn publish(&self, token: &str, exposed: Exposed) {
61 if let Ok(mut entries) = self.entries.write() {
62 entries.insert(token.to_string(), exposed);
63 }
64 }
65
66 pub fn set_enabled(&self, token: &str, enabled: bool) {
68 if let Ok(mut entries) = self.entries.write()
69 && let Some(entry) = entries.get_mut(token)
70 {
71 entry.enabled = enabled;
72 }
73 }
74
75 pub fn withdraw(&self, token: &str) {
77 if let Ok(mut entries) = self.entries.write() {
78 entries.remove(token);
79 }
80 }
81}
82
83impl Exposures for StaticExposures {
84 fn resolve(&self, token: &str) -> Option<Exposed> {
85 self.entries.read().ok()?.get(token).cloned()
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use gfeh_core::{NodeId, PartitionId};
93
94 fn exposed() -> Exposed {
95 Exposed {
96 node: NodeRef::Id(PartitionId(uuid_nil()), NodeId(7)),
99 filename: None,
100 enabled: true,
101 }
102 }
103
104 fn uuid_nil() -> uuid::Uuid {
106 uuid::Uuid::nil()
107 }
108
109 #[test]
110 fn a_published_token_resolves_and_an_unknown_one_does_not() {
111 let table = StaticExposures::new().with("tok", exposed());
112 assert_eq!(table.resolve("tok"), Some(exposed()));
113 assert_eq!(table.resolve("other"), None);
114 }
115
116 #[test]
117 fn disabling_keeps_the_token_and_clears_the_link() {
118 let table = StaticExposures::new().with("tok", exposed());
119 table.set_enabled("tok", false);
120 assert_eq!(table.resolve("tok").map(|e| e.enabled), Some(false));
121
122 table.set_enabled("tok", true);
123 assert_eq!(table.resolve("tok").map(|e| e.enabled), Some(true));
124 }
125
126 #[test]
127 fn withdrawing_removes_it() {
128 let table = StaticExposures::new().with("tok", exposed());
129 table.withdraw("tok");
130 assert_eq!(table.resolve("tok"), None);
131 }
132}