1use crate::error::{Error, Result};
4use reqwest::Client as HttpClient;
5use serde::de::DeserializeOwned;
6use std::time::Duration;
7use yldfi_common::http::HttpClientConfig;
8
9const DEFAULT_BASE_URL: &str = "https://api.curve.finance/v1";
10
11#[derive(Debug, Clone)]
13pub struct Config {
14 pub base_url: String,
16 pub http: HttpClientConfig,
18}
19
20impl Default for Config {
21 fn default() -> Self {
22 Self {
23 base_url: DEFAULT_BASE_URL.to_string(),
24 http: HttpClientConfig::default(),
25 }
26 }
27}
28
29impl Config {
30 #[must_use]
32 pub fn new() -> Self {
33 Self::default()
34 }
35
36 #[must_use]
38 pub fn with_base_url(mut self, url: impl Into<String>) -> Self {
39 self.base_url = url.into();
40 self
41 }
42
43 #[must_use]
45 pub fn with_timeout(mut self, timeout: Duration) -> Self {
46 self.http.timeout = timeout;
47 self
48 }
49
50 #[must_use]
52 pub fn with_proxy(mut self, proxy: impl Into<String>) -> Self {
53 self.http.proxy = Some(proxy.into());
54 self
55 }
56
57 #[must_use]
59 pub fn with_optional_proxy(mut self, proxy: Option<String>) -> Self {
60 self.http.proxy = proxy;
61 self
62 }
63}
64
65#[derive(Debug, Clone)]
67pub struct Client {
68 http: HttpClient,
69 base_url: String,
70}
71
72impl Client {
73 pub fn new() -> Result<Self> {
75 Self::with_config(Config::default())
76 }
77
78 pub fn with_config(config: Config) -> Result<Self> {
80 let http = yldfi_common::build_client(&config.http)?;
81
82 Ok(Self {
83 http,
84 base_url: config.base_url,
85 })
86 }
87
88 pub(crate) async fn get<T: DeserializeOwned>(&self, path: &str) -> Result<T> {
90 let url = format!("{}{}", self.base_url, path);
91 let response = self.http.get(&url).send().await?;
92
93 let status = response.status().as_u16();
94
95 if status == 429 {
97 let retry_after = response
98 .headers()
99 .get("retry-after")
100 .and_then(|v| v.to_str().ok())
101 .and_then(|v| v.parse().ok());
102 return Err(Error::rate_limited(retry_after));
103 }
104
105 if !response.status().is_success() {
106 let message = response.text().await.unwrap_or_default();
107 return Err(Error::api(status, message));
108 }
109
110 let data = response.json().await?;
111 Ok(data)
112 }
113}