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}