auto_discovery/simple.rs
1//! Simplified API for common use cases
2//!
3//! This module provides simplified, zero-configuration APIs for the most common
4//! service discovery scenarios.
5
6use crate::{
7 config::DiscoveryConfig,
8 error::Result,
9 service::ServiceInfo,
10 types::{ServiceType, ProtocolType},
11 ServiceDiscovery,
12};
13use std::time::Duration;
14
15/// Simple service discovery with sensible defaults
16pub struct SimpleDiscovery {
17 inner: ServiceDiscovery,
18}
19
20impl SimpleDiscovery {
21 /// Create a new simple discovery instance with defaults
22 ///
23 /// Automatically configures:
24 /// - mDNS protocol
25 /// - Common service types (_http._tcp, _https._tcp, _ssh._tcp, _ftp._tcp)
26 /// - 5-second timeout
27 /// - Service verification enabled
28 ///
29 /// # Example
30 /// ```rust
31 /// use auto_discovery::simple::SimpleDiscovery;
32 ///
33 /// #[tokio::main]
34 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
35 /// let discovery = SimpleDiscovery::new().await?;
36 /// let services = discovery.discover_all().await?;
37 /// println!("Found {} services", services.len());
38 /// Ok(())
39 /// }
40 /// ```
41 pub async fn new() -> Result<Self> {
42 let config = DiscoveryConfig::new()
43 .with_service_type(ServiceType::new("_http._tcp")?)
44 .with_service_type(ServiceType::new("_https._tcp")?)
45 .with_service_type(ServiceType::new("_ssh._tcp")?)
46 .with_service_type(ServiceType::new("_ftp._tcp")?)
47 .with_protocol(ProtocolType::Mdns)
48 .with_timeout(Duration::from_secs(5))
49 .with_verify_services(true);
50
51 let inner = ServiceDiscovery::new(config).await?;
52 Ok(Self { inner })
53 }
54
55 /// Discover all configured services
56 pub async fn discover_all(&self) -> Result<Vec<ServiceInfo>> {
57 self.inner.discover_services(None).await
58 }
59
60 /// Discover only HTTP services
61 pub async fn discover_http(&self) -> Result<Vec<ServiceInfo>> {
62 self.inner.discover_services(Some(ProtocolType::Mdns)).await
63 }
64
65 /// Register a simple HTTP service
66 ///
67 /// # Example
68 /// ```rust
69 /// # use auto_discovery::simple::SimpleDiscovery;
70 /// # #[tokio::main]
71 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
72 /// let discovery = SimpleDiscovery::new().await?;
73 /// discovery.register_http_service("My Web App", 8080).await?;
74 /// # Ok(())
75 /// # }
76 /// ```
77 pub async fn register_http_service(&self, name: &str, port: u16) -> Result<()> {
78 let service = ServiceInfo::new(name, "_http._tcp", port, None)?;
79 self.inner.register_service(service).await
80 }
81
82 /// Register a service with custom attributes
83 pub async fn register_service_with_attributes(
84 &self,
85 name: &str,
86 service_type: &str,
87 port: u16,
88 attributes: Vec<(&str, &str)>
89 ) -> Result<()> {
90 let service = ServiceInfo::new(name, service_type, port, Some(attributes))?;
91 self.inner.register_service(service).await
92 }
93
94 /// Stop all services and cleanup
95 pub async fn shutdown(&self) -> Result<()> {
96 // Unregister all services
97 let services = self.inner.get_registered_services().await;
98 for service in services {
99 let _ = self.inner.unregister_service(&service).await;
100 }
101 Ok(())
102 }
103}
104
105/// Quick one-liner functions for common scenarios
106///
107/// Discover all HTTP services on the network
108///
109/// # Example
110/// ```rust
111/// use auto_discovery::simple::discover_http_services;
112///
113/// #[tokio::main]
114/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
115/// let services = discover_http_services().await?;
116/// for service in services {
117/// println!("Found: {} at {}:{}", service.name(), service.address, service.port);
118/// }
119/// Ok(())
120/// }
121/// ```
122pub async fn discover_http_services() -> Result<Vec<ServiceInfo>> {
123 let discovery = SimpleDiscovery::new().await?;
124 discovery.discover_http().await
125}
126
127/// Register an HTTP service and return a handle for cleanup
128///
129/// # Example
130/// ```rust
131/// use auto_discovery::simple::register_http_service;
132///
133/// #[tokio::main]
134/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
135/// let handle = register_http_service("My API", 8080).await?;
136///
137/// // Your server code here
138///
139/// handle.unregister().await?;
140/// Ok(())
141/// }
142/// ```
143pub async fn register_http_service(name: &str, port: u16) -> Result<ServiceHandle> {
144 let discovery = SimpleDiscovery::new().await?;
145 let service = ServiceInfo::new(name, "_http._tcp", port, None)?;
146 discovery.inner.register_service(service.clone()).await?;
147 Ok(ServiceHandle {
148 discovery: discovery.inner,
149 service,
150 })
151}
152
153/// Handle for managing a registered service
154pub struct ServiceHandle {
155 discovery: ServiceDiscovery,
156 service: ServiceInfo,
157}
158
159impl ServiceHandle {
160 /// Unregister the service
161 pub async fn unregister(self) -> Result<()> {
162 self.discovery.unregister_service(&self.service).await
163 }
164
165 /// Get service information
166 pub fn service(&self) -> &ServiceInfo {
167 &self.service
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[tokio::test]
176 async fn test_simple_discovery() {
177 let discovery = SimpleDiscovery::new().await.unwrap();
178
179 // Should not fail even if no services found
180 let services = discovery.discover_all().await.unwrap();
181 // Can't assert specific count since it depends on network
182 assert!(!services.is_empty() || services.is_empty()); // services can be empty in test environment
183 }
184
185 #[tokio::test]
186 async fn test_register_http_service() {
187 let discovery = SimpleDiscovery::new().await.unwrap();
188 let result = discovery.register_http_service("Test Service", 8080).await;
189
190 // Registration might fail in test environment
191 match result {
192 Ok(_) => {
193 // Service registered successfully
194 }
195 Err(_) => {
196 // Expected in test environment without actual mDNS
197 }
198 }
199 }
200
201 #[tokio::test]
202 async fn test_one_liner_functions() {
203 // Test the one-liner function
204 let result = discover_http_services().await;
205
206 // Should not panic even if discovery fails
207 match result {
208 Ok(_services) => {
209 // Success case
210 }
211 Err(_) => {
212 // Expected in test environment
213 }
214 }
215 }
216}