aimdb_client/
discovery.rs1use crate::engine::AimxConnection;
6use crate::error::{ClientError, ClientResult};
7use crate::protocol::WelcomeMessage;
8use std::path::{Path, PathBuf};
9use std::time::Duration;
10
11const SOCKET_SEARCH_DIRS: &[&str] = &["/tmp", "/var/run/aimdb"];
13
14#[derive(Debug, Clone)]
16pub struct InstanceInfo {
17 pub endpoint: PathBuf,
18 pub server_version: String,
19 pub protocol_version: String,
20 pub permissions: Vec<String>,
21 pub writable_records: Vec<String>,
22 pub max_subscriptions: Option<usize>,
23 pub authenticated: bool,
24}
25
26impl From<(PathBuf, WelcomeMessage)> for InstanceInfo {
27 fn from((endpoint, welcome): (PathBuf, WelcomeMessage)) -> Self {
28 Self {
29 endpoint,
30 server_version: welcome.server,
31 protocol_version: welcome.version,
32 permissions: welcome.permissions,
33 writable_records: welcome.writable_records,
34 max_subscriptions: welcome.max_subscriptions,
35 authenticated: welcome.authenticated.unwrap_or(false),
36 }
37 }
38}
39
40pub async fn discover_instances() -> ClientResult<Vec<InstanceInfo>> {
42 let mut instances = Vec::new();
43
44 for dir_path in SOCKET_SEARCH_DIRS {
45 if let Ok(entries) = tokio::fs::read_dir(dir_path).await {
46 instances.extend(scan_directory(entries).await);
47 }
48 }
49
50 if instances.is_empty() {
51 return Err(ClientError::NoInstancesFound);
52 }
53
54 Ok(instances)
55}
56
57async fn scan_directory(mut entries: tokio::fs::ReadDir) -> Vec<InstanceInfo> {
59 let mut instances = Vec::new();
60
61 while let Ok(Some(entry)) = entries.next_entry().await {
62 let path = entry.path();
63
64 if path.extension().and_then(|s| s.to_str()) == Some("sock") {
66 if let Ok(info) = probe_instance(&path).await {
68 instances.push(info);
69 }
70 }
71 }
72
73 instances
74}
75
76async fn probe_instance(socket_path: &Path) -> ClientResult<InstanceInfo> {
78 let connect_timeout = Duration::from_millis(500);
82 let endpoint = socket_path.to_string_lossy();
84 let client = AimxConnection::connect_with_timeout(&endpoint, connect_timeout).await?;
85
86 let welcome = client.server_info().clone();
87
88 Ok(InstanceInfo::from((socket_path.to_path_buf(), welcome)))
89}
90
91pub async fn find_instance(socket_hint: Option<&str>) -> ClientResult<InstanceInfo> {
93 if let Some(socket_path) = socket_hint {
95 let path = PathBuf::from(socket_path);
96 if path.exists() {
97 return probe_instance(&path).await;
98 } else {
99 return Err(ClientError::connection_failed(
100 socket_path.to_string(),
101 "socket file does not exist",
102 ));
103 }
104 }
105
106 let instances = discover_instances().await?;
108
109 instances
110 .into_iter()
111 .next()
112 .ok_or(ClientError::NoInstancesFound)
113}