aimdb_core/remote/
config.rs1use core::any::TypeId;
4use std::{collections::HashSet, path::PathBuf, string::String, vec::Vec};
5
6#[derive(Debug, Clone)]
11pub struct AimxConfig {
12 pub socket_path: PathBuf,
14
15 pub security_policy: SecurityPolicy,
17
18 pub max_connections: usize,
20
21 pub subscription_queue_size: usize,
23
24 pub auth_token: Option<String>,
26
27 pub socket_permissions: Option<u32>,
30}
31
32impl AimxConfig {
33 pub fn uds_default() -> Self {
43 Self {
44 socket_path: PathBuf::from("/tmp/aimdb.sock"),
45 security_policy: SecurityPolicy::ReadOnly,
46 max_connections: 16,
47 subscription_queue_size: 100,
48 auth_token: None,
49 socket_permissions: Some(0o600),
50 }
51 }
52
53 pub fn socket_path(mut self, path: impl Into<PathBuf>) -> Self {
55 self.socket_path = path.into();
56 self
57 }
58
59 pub fn security_policy(mut self, policy: SecurityPolicy) -> Self {
61 self.security_policy = policy;
62 self
63 }
64
65 pub fn max_connections(mut self, max: usize) -> Self {
67 self.max_connections = max;
68 self
69 }
70
71 pub fn subscription_queue_size(mut self, size: usize) -> Self {
73 self.subscription_queue_size = size;
74 self
75 }
76
77 pub fn auth_token(mut self, token: impl Into<String>) -> Self {
79 self.auth_token = Some(token.into());
80 self
81 }
82
83 pub fn socket_permissions(mut self, mode: u32) -> Self {
91 self.socket_permissions = Some(mode);
92 self
93 }
94}
95
96#[derive(Debug, Clone)]
100pub enum SecurityPolicy {
101 ReadOnly,
106
107 ReadWrite {
112 writable_records: HashSet<TypeId>,
114 },
115}
116
117impl SecurityPolicy {
118 pub fn read_only() -> Self {
120 Self::ReadOnly
121 }
122
123 pub fn read_write() -> Self {
125 Self::ReadWrite {
126 writable_records: HashSet::new(),
127 }
128 }
129
130 pub fn allow_write<T: 'static>(&mut self) {
134 match self {
135 Self::ReadWrite { writable_records } => {
136 writable_records.insert(TypeId::of::<T>());
137 }
138 Self::ReadOnly => {
139 panic!("Cannot allow writes in ReadOnly security policy");
140 }
141 }
142 }
143
144 pub fn is_writable(&self, type_id: TypeId) -> bool {
146 match self {
147 Self::ReadOnly => false,
148 Self::ReadWrite { writable_records } => writable_records.contains(&type_id),
149 }
150 }
151
152 pub fn permissions(&self) -> &[&str] {
154 match self {
155 Self::ReadOnly => &["read", "subscribe"],
156 Self::ReadWrite { .. } => &["read", "subscribe", "write"],
157 }
158 }
159
160 pub fn writable_records(&self) -> Vec<TypeId> {
162 match self {
163 Self::ReadOnly => Vec::new(),
164 Self::ReadWrite { writable_records } => writable_records.iter().copied().collect(),
165 }
166 }
167}
168
169impl SecurityPolicy {
171 pub fn with_writable_record<T: 'static>(mut self) -> Self {
173 if let Self::ReadWrite {
174 ref mut writable_records,
175 } = self
176 {
177 writable_records.insert(TypeId::of::<T>());
178 }
179 self
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 #[test]
188 #[cfg(feature = "std")]
189 fn test_default_config() {
190 let config = AimxConfig::uds_default();
191 assert_eq!(config.socket_path, PathBuf::from("/tmp/aimdb.sock"));
192 assert_eq!(config.max_connections, 16);
193 assert_eq!(config.subscription_queue_size, 100);
194 assert!(matches!(config.security_policy, SecurityPolicy::ReadOnly));
195 assert!(config.auth_token.is_none());
196 }
197
198 #[test]
199 #[cfg(feature = "std")]
200 fn test_config_builder() {
201 let config = AimxConfig::uds_default()
202 .socket_path("/var/run/aimdb.sock")
203 .max_connections(32)
204 .subscription_queue_size(200)
205 .auth_token("secret-token")
206 .socket_permissions(0o660);
207
208 assert_eq!(config.socket_path, PathBuf::from("/var/run/aimdb.sock"));
209 assert_eq!(config.max_connections, 32);
210 assert_eq!(config.subscription_queue_size, 200);
211 assert_eq!(config.auth_token, Some("secret-token".to_string()));
212 assert_eq!(config.socket_permissions, Some(0o660));
213 }
214
215 #[test]
216 fn test_security_policy_read_only() {
217 let policy = SecurityPolicy::read_only();
218 assert!(!policy.is_writable(TypeId::of::<i32>()));
219 assert_eq!(policy.permissions(), &["read", "subscribe"]);
220 }
221
222 #[test]
223 fn test_security_policy_read_write() {
224 let mut policy = SecurityPolicy::read_write();
225 assert!(!policy.is_writable(TypeId::of::<i32>()));
226
227 policy.allow_write::<i32>();
228 assert!(policy.is_writable(TypeId::of::<i32>()));
229 assert!(!policy.is_writable(TypeId::of::<String>()));
230 assert_eq!(policy.permissions(), &["read", "subscribe", "write"]);
231 }
232
233 #[test]
234 #[should_panic(expected = "Cannot allow writes in ReadOnly security policy")]
235 fn test_security_policy_read_only_panic() {
236 let mut policy = SecurityPolicy::read_only();
237 policy.allow_write::<i32>();
238 }
239
240 #[test]
241 fn test_security_policy_builder() {
242 let policy = SecurityPolicy::read_write()
243 .with_writable_record::<i32>()
244 .with_writable_record::<String>();
245
246 assert!(policy.is_writable(TypeId::of::<i32>()));
247 assert!(policy.is_writable(TypeId::of::<String>()));
248 assert!(!policy.is_writable(TypeId::of::<f64>()));
249 }
250}