Skip to main content

legend_client/
client.rs

1use std::sync::Arc;
2
3use reqwest::{Client, Method};
4use serde::{Serialize, de::DeserializeOwned};
5
6use crate::accounts::AccountsApi;
7use crate::error::{LegendPrimeError, Result};
8use crate::plan::PlanApi;
9use crate::types::Config;
10
11const DEFAULT_BASE_URL: &str = "https://prime-api.legend.xyz";
12
13pub(crate) struct ClientInner {
14    http: Client,
15    base_url: String,
16    query_key: String,
17    verbose: bool,
18}
19
20impl ClientInner {
21    pub(crate) async fn request<T: DeserializeOwned>(
22        &self,
23        method: Method,
24        path: &str,
25        body: Option<&(impl Serialize + Sync)>,
26    ) -> Result<T> {
27        let url = format!("{}{}", self.base_url, path);
28
29        if self.verbose {
30            eprintln!("[verbose] {} {}", method, url);
31            if let Some(body) = &body {
32                if let Ok(json) = serde_json::to_string(body) {
33                    eprintln!("[verbose] body: {}", json);
34                }
35            }
36        }
37
38        let mut req = self
39            .http
40            .request(method, &url)
41            .header("Authorization", format!("Bearer {}", self.query_key))
42            .header("Accept", "application/json");
43
44        if let Some(body) = body {
45            req = req.json(body);
46        }
47
48        let res = req.send().await.map_err(LegendPrimeError::Http)?;
49        let status = res.status();
50
51        if self.verbose {
52            eprintln!("[verbose] response: {}", status);
53        }
54
55        if !status.is_success() {
56            let raw_body = res.text().await.unwrap_or_default();
57
58            if self.verbose {
59                eprintln!("[verbose] error body: {}", raw_body);
60            }
61
62            let err_body: serde_json::Value = serde_json::from_str(&raw_body)
63                .unwrap_or_else(|_| serde_json::json!({"code": "unknown", "detail": raw_body}));
64
65            let details = err_body.get("details").cloned();
66
67            return Err(LegendPrimeError::Api {
68                code: err_body["code"].as_str().unwrap_or("unknown").to_string(),
69                message: err_body["detail"]
70                    .as_str()
71                    .unwrap_or(&raw_body)
72                    .to_string(),
73                status: status.as_u16(),
74                details,
75            });
76        }
77
78        let raw_body = res.text().await.map_err(LegendPrimeError::Http)?;
79
80        if self.verbose {
81            eprintln!(
82                "[verbose] response body: {}",
83                &raw_body[..raw_body.len().min(500)]
84            );
85        }
86
87        serde_json::from_str(&raw_body).map_err(LegendPrimeError::Deserialize)
88    }
89}
90
91pub struct LegendPrime {
92    pub(crate) inner: Arc<ClientInner>,
93    pub accounts: AccountsApi,
94    pub plan: PlanApi,
95}
96
97impl LegendPrime {
98    pub fn new(config: Config) -> Self {
99        let base_url = config
100            .base_url
101            .unwrap_or_else(|| DEFAULT_BASE_URL.to_string());
102        let http = Client::new();
103        let inner = Arc::new(ClientInner {
104            http,
105            base_url,
106            query_key: config.query_key,
107            verbose: config.verbose,
108        });
109
110        Self {
111            accounts: AccountsApi {
112                inner: inner.clone(),
113            },
114            plan: PlanApi {
115                inner: inner.clone(),
116            },
117            inner,
118        }
119    }
120}