//! Base handle resolver implementation. //! //! This module provides the fundamental handle resolution implementation that //! performs actual DNS and HTTP lookups to resolve AT Protocol handles to DIDs. use super::errors::HandleResolverError; use super::traits::HandleResolver; use crate::metrics::SharedMetricsPublisher; use async_trait::async_trait; use atproto_identity::resolve::{DnsResolver, resolve_subject}; use reqwest::Client; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; /// Base handle resolver that performs actual resolution via DNS and HTTP. /// /// This resolver implements the core AT Protocol handle resolution logic: /// 1. DNS TXT record lookup for `_atproto.{handle}` /// 2. HTTP well-known endpoint query at `https://{handle}/.well-known/atproto-did` /// 3. DID document retrieval from PLC directory or web DIDs /// /// # Example /// /// ```no_run /// use std::sync::Arc; /// use reqwest::Client; /// use atproto_identity::resolve::HickoryDnsResolver; /// use quickdid::handle_resolver::{create_base_resolver, HandleResolver}; /// use quickdid::metrics::NoOpMetricsPublisher; /// /// # async fn example() { /// let dns_resolver = Arc::new(HickoryDnsResolver::create_resolver(&[])); /// let http_client = Client::new(); /// let metrics = Arc::new(NoOpMetricsPublisher); /// /// let resolver = create_base_resolver( /// dns_resolver, /// http_client, /// metrics, /// ); /// /// let (did, timestamp) = resolver.resolve("alice.bsky.social").await.unwrap(); /// println!("Resolved {} at {}", did, timestamp); /// # } /// ``` pub(super) struct BaseHandleResolver { /// DNS resolver for handle-to-DID resolution via TXT records. dns_resolver: Arc, /// HTTP client for DID document retrieval and well-known endpoint queries. http_client: Client, /// Metrics publisher for telemetry. metrics: SharedMetricsPublisher, } #[async_trait] impl HandleResolver for BaseHandleResolver { async fn resolve(&self, s: &str) -> Result<(String, u64), HandleResolverError> { let start_time = std::time::Instant::now(); // Perform DNS/HTTP resolution let result = resolve_subject(&self.http_client, &*self.dns_resolver, s) .await .map_err(|e| HandleResolverError::ResolutionFailed(e.to_string())); let duration_ms = start_time.elapsed().as_millis() as u64; // Publish metrics match result { Ok(did) => { self.metrics .time_with_tags( "resolver.base.duration_ms", duration_ms, &[("success", "1")], ) .await; let timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) .map_err(|e| { HandleResolverError::ResolutionFailed(format!("System time error: {}", e)) })? .as_secs(); Ok((did, timestamp)) } Err(e) => { self.metrics .time_with_tags( "resolver.base.duration_ms", duration_ms, &[("success", "0")], ) .await; Err(e) } } } } /// Create a new base handle resolver. /// /// This factory function creates a resolver that performs actual DNS and HTTP /// lookups for handle resolution. /// /// # Arguments /// /// * `dns_resolver` - DNS resolver for TXT record lookups /// * `http_client` - HTTP client for well-known endpoint queries /// * `metrics` - Metrics publisher for telemetry pub fn create_base_resolver( dns_resolver: Arc, http_client: Client, metrics: SharedMetricsPublisher, ) -> Arc { Arc::new(BaseHandleResolver { dns_resolver, http_client, metrics, }) }