1use crate::types::identity::LdpIdentityCard;
7use crate::types::messages::LdpEnvelope;
8use reqwest::Client;
9use serde_json::Value;
10use tracing::{debug, instrument};
11
12#[derive(Debug, Clone)]
14pub struct LdpClient {
15 http: Client,
16}
17
18impl LdpClient {
19 pub fn new() -> Self {
21 Self {
22 http: Client::new(),
23 }
24 }
25
26 pub fn with_http_client(http: Client) -> Self {
28 Self { http }
29 }
30
31 #[instrument(skip(self, message), fields(url = %url, msg_type = ?std::mem::discriminant(&message.body)))]
35 pub async fn send_message(
36 &self,
37 url: &str,
38 message: &LdpEnvelope,
39 ) -> Result<LdpEnvelope, String> {
40 let endpoint = format!("{}/ldp/messages", url.trim_end_matches('/'));
41
42 debug!(endpoint = %endpoint, "Sending LDP message");
43
44 let response = self
45 .http
46 .post(&endpoint)
47 .json(message)
48 .send()
49 .await
50 .map_err(|e| format!("LDP HTTP request failed: {e}"))?;
51
52 if !response.status().is_success() {
53 let status = response.status();
54 let body = response
55 .text()
56 .await
57 .unwrap_or_else(|_| "unable to read body".into());
58 return Err(format!("LDP request failed ({}): {}", status, body));
59 }
60
61 let envelope: LdpEnvelope = response
62 .json()
63 .await
64 .map_err(|e| format!("Failed to parse LDP response: {e}"))?;
65
66 Ok(envelope)
67 }
68
69 #[instrument(skip(self), fields(url = %url))]
73 pub async fn fetch_identity_card(
74 &self,
75 url: &str,
76 ) -> Result<LdpIdentityCard, String> {
77 let endpoint = format!("{}/ldp/identity", url.trim_end_matches('/'));
78
79 debug!(endpoint = %endpoint, "Fetching LDP identity card");
80
81 let response = self
82 .http
83 .get(&endpoint)
84 .send()
85 .await
86 .map_err(|e| format!("Failed to fetch identity card: {e}"))?;
87
88 if !response.status().is_success() {
89 let status = response.status();
90 return Err(format!("Identity card fetch failed ({})", status));
91 }
92
93 let card: LdpIdentityCard = response
94 .json()
95 .await
96 .map_err(|e| format!("Failed to parse identity card: {e}"))?;
97
98 Ok(card)
99 }
100
101 pub async fn fetch_identity_wellknown(&self, url: &str) -> Result<LdpIdentityCard, String> {
105 let wellknown = format!("{}/.well-known/ldp-identity", url.trim_end_matches('/'));
106
107 match self.http.get(&wellknown).send().await {
108 Ok(resp) if resp.status().is_success() => {
109 resp.json().await.map_err(|e| format!("Failed to parse identity: {e}"))
110 }
111 _ => self.fetch_identity_card(url).await,
112 }
113 }
114
115 #[instrument(skip(self), fields(url = %url))]
119 pub async fn fetch_capabilities(&self, url: &str) -> Result<Value, String> {
120 let endpoint = format!("{}/ldp/capabilities", url.trim_end_matches('/'));
121
122 let response = self
123 .http
124 .get(&endpoint)
125 .send()
126 .await
127 .map_err(|e| format!("Failed to fetch capabilities: {e}"))?;
128
129 if !response.status().is_success() {
130 let status = response.status();
131 return Err(format!("Capabilities fetch failed ({})", status));
132 }
133
134 response
135 .json()
136 .await
137 .map_err(|e| format!("Failed to parse capabilities: {e}"))
138 }
139}
140
141impl Default for LdpClient {
142 fn default() -> Self {
143 Self::new()
144 }
145}