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            return Err(LegendPrimeError::Api {
66                code: err_body["code"].as_str().unwrap_or("unknown").to_string(),
67                message: err_body["detail"]
68                    .as_str()
69                    .unwrap_or(&raw_body)
70                    .to_string(),
71                status: status.as_u16(),
72            });
73        }
74
75        let raw_body = res.text().await.map_err(LegendPrimeError::Http)?;
76
77        if self.verbose {
78            eprintln!(
79                "[verbose] response body: {}",
80                &raw_body[..raw_body.len().min(500)]
81            );
82        }
83
84        serde_json::from_str(&raw_body).map_err(LegendPrimeError::Deserialize)
85    }
86}
87
88pub struct LegendPrime {
89    pub(crate) inner: Arc<ClientInner>,
90    pub accounts: AccountsApi,
91    pub plan: PlanApi,
92}
93
94impl LegendPrime {
95    pub fn new(config: Config) -> Self {
96        let base_url = config
97            .base_url
98            .unwrap_or_else(|| DEFAULT_BASE_URL.to_string());
99        let http = Client::new();
100        let inner = Arc::new(ClientInner {
101            http,
102            base_url,
103            query_key: config.query_key,
104            verbose: config.verbose,
105        });
106
107        Self {
108            accounts: AccountsApi {
109                inner: inner.clone(),
110            },
111            plan: PlanApi {
112                inner: inner.clone(),
113            },
114            inner,
115        }
116    }
117}