atproto_client/
record_resolver.rs1use std::str::FromStr;
4use std::sync::Arc;
5
6use anyhow::{Result, anyhow, bail};
7use async_trait::async_trait;
8use atproto_identity::traits::IdentityResolver;
9use atproto_record::aturi::ATURI;
10
11use crate::{
12 client::Auth,
13 com::atproto::repo::{GetRecordResponse, get_record},
14};
15
16#[async_trait]
21pub trait RecordResolver: Send + Sync {
22 async fn resolve<T>(&self, aturi: &str) -> Result<T>
24 where
25 T: serde::de::DeserializeOwned + Send;
26}
27
28#[derive(Clone)]
32pub struct HttpRecordResolver {
33 http_client: reqwest::Client,
34 identity_resolver: Arc<dyn IdentityResolver>,
35}
36
37impl HttpRecordResolver {
38 pub fn new(http_client: reqwest::Client, identity_resolver: Arc<dyn IdentityResolver>) -> Self {
43 Self {
44 http_client,
45 identity_resolver,
46 }
47 }
48}
49
50#[async_trait]
51impl RecordResolver for HttpRecordResolver {
52 async fn resolve<T>(&self, aturi: &str) -> Result<T>
53 where
54 T: serde::de::DeserializeOwned + Send,
55 {
56 let parsed = ATURI::from_str(aturi).map_err(|error| anyhow!(error))?;
57
58 let document = self
60 .identity_resolver
61 .resolve(&parsed.authority)
62 .await
63 .map_err(|error| {
64 anyhow!(
65 "Failed to resolve identity for {}: {}",
66 parsed.authority,
67 error
68 )
69 })?;
70
71 let pds_endpoints = document.pds_endpoints();
73 let base_url = pds_endpoints
74 .first()
75 .ok_or_else(|| anyhow!("No PDS endpoint found for {}", parsed.authority))?;
76
77 let auth = Auth::None;
78
79 let response = get_record(
80 &self.http_client,
81 &auth,
82 base_url,
83 &parsed.authority,
84 &parsed.collection,
85 &parsed.record_key,
86 None,
87 )
88 .await?;
89
90 match response {
91 GetRecordResponse::Record { value, .. } => {
92 serde_json::from_value(value).map_err(|error| anyhow!(error))
93 }
94 GetRecordResponse::Error(error) => {
95 let message = error.error_message();
96 if message.is_empty() {
97 bail!("Record resolution failed without additional error details");
98 }
99
100 bail!(message);
101 }
102 }
103 }
104}