use nadisc::managed::{
Capabilities, DiscoveryError, ErrorCode, EvidenceKind, ManagedProvider, ProviderSnapshot,
Result,
};
use nadisc::{DiscoveryClient, DnsDiscovery, DnsService, Instance, StaticDiscovery};
use std::{
collections::BTreeMap,
sync::atomic::{AtomicBool, Ordering},
};
enum Backend {
Static(StaticDiscovery),
Dns(DnsDiscovery),
}
pub struct LocalProvider {
backend: Backend,
closed: AtomicBool,
}
impl LocalProvider {
pub fn static_services(services: BTreeMap<String, Vec<Instance>>) -> Result<Self> {
let backend = StaticDiscovery::new(services)
.map_err(|_| crate::config::invalid("invalid static discovery table"))?;
Ok(Self {
backend: Backend::Static(backend),
closed: AtomicBool::new(false),
})
}
pub fn dns_services(services: BTreeMap<String, DnsService>) -> Result<Self> {
if services.is_empty() {
return Err(crate::config::invalid(
"DNS service bindings cannot be empty",
));
}
let backend = DnsDiscovery::new(services)
.map_err(|_| crate::config::invalid("invalid DNS discovery table"))?;
Ok(Self {
backend: Backend::Dns(backend),
closed: AtomicBool::new(false),
})
}
fn check(&self) -> Result<()> {
if self.closed.load(Ordering::Acquire) {
Err(crate::closed())
} else {
Ok(())
}
}
}
#[async_trait::async_trait]
impl ManagedProvider for LocalProvider {
fn capabilities(&self) -> Capabilities {
Capabilities {
registration: false,
enumeration: true,
evidence: match self.backend {
Backend::Static(_) => EvidenceKind::Static,
Backend::Dns(_) => EvidenceKind::Address,
},
}
}
async fn prepare(&self) -> Result<()> {
self.check()
}
async fn observe(&self, service: &str) -> Result<ProviderSnapshot> {
self.check()?;
let instances = match &self.backend {
Backend::Static(provider) => provider.discover_all(service).await,
Backend::Dns(provider) => provider.discover_all(service).await,
}
.map_err(|_| {
DiscoveryError::new(ErrorCode::Unavailable, "local provider observation failed")
})?;
self.check()?;
Ok(ProviderSnapshot {
instances,
evidence: self.capabilities().evidence,
})
}
async fn services(&self) -> Result<Vec<String>> {
self.check()?;
match &self.backend {
Backend::Static(provider) => provider.list_services().await,
Backend::Dns(provider) => provider.list_services().await,
}
.map_err(|_| {
DiscoveryError::new(ErrorCode::Unavailable, "local service enumeration failed")
})
}
async fn close(&self) -> Result<()> {
self.closed.store(true, Ordering::Release);
Ok(())
}
}