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(&self, url: &str) -> Result<LdpIdentityCard, String> {
74 let endpoint = format!("{}/ldp/identity", url.trim_end_matches('/'));
75
76 debug!(endpoint = %endpoint, "Fetching LDP identity card");
77
78 let response = self
79 .http
80 .get(&endpoint)
81 .send()
82 .await
83 .map_err(|e| format!("Failed to fetch identity card: {e}"))?;
84
85 if !response.status().is_success() {
86 let status = response.status();
87 return Err(format!("Identity card fetch failed ({})", status));
88 }
89
90 let card: LdpIdentityCard = response
91 .json()
92 .await
93 .map_err(|e| format!("Failed to parse identity card: {e}"))?;
94
95 Ok(card)
96 }
97
98 pub async fn fetch_identity_wellknown(&self, url: &str) -> Result<LdpIdentityCard, String> {
102 let wellknown = format!("{}/.well-known/ldp-identity", url.trim_end_matches('/'));
103
104 match self.http.get(&wellknown).send().await {
105 Ok(resp) if resp.status().is_success() => resp
106 .json()
107 .await
108 .map_err(|e| format!("Failed to parse identity: {e}")),
109 _ => self.fetch_identity_card(url).await,
110 }
111 }
112
113 #[instrument(skip(self), fields(url = %url))]
117 pub async fn fetch_capabilities(&self, url: &str) -> Result<Value, String> {
118 let endpoint = format!("{}/ldp/capabilities", url.trim_end_matches('/'));
119
120 let response = self
121 .http
122 .get(&endpoint)
123 .send()
124 .await
125 .map_err(|e| format!("Failed to fetch capabilities: {e}"))?;
126
127 if !response.status().is_success() {
128 let status = response.status();
129 return Err(format!("Capabilities fetch failed ({})", status));
130 }
131
132 response
133 .json()
134 .await
135 .map_err(|e| format!("Failed to parse capabilities: {e}"))
136 }
137}
138
139impl Default for LdpClient {
140 fn default() -> Self {
141 Self::new()
142 }
143}