use reqwest::Client;
use url::Url;
use crate::error::{PrecolatorError, Result};
use crate::models::*;
#[derive(Debug, Clone)]
pub struct PrecolatorClient {
http: Client,
base_url: Url,
}
impl PrecolatorClient {
pub fn new(base_url: &str) -> Self {
let base = Url::parse(base_url).expect("invalid base URL");
Self {
http: Client::builder()
.user_agent(format!("precolator-sdk/{}", crate::SDK_VERSION))
.build()
.expect("failed to build HTTP client"),
base_url: base,
}
}
pub fn with_client(base_url: &str, http: Client) -> Result<Self> {
Ok(Self {
http,
base_url: Url::parse(base_url)?,
})
}
fn url(&self, path: &str) -> Result<Url> {
self.base_url
.join(path)
.map_err(PrecolatorError::InvalidUrl)
}
async fn get<T: serde::de::DeserializeOwned>(&self, path: &str) -> Result<T> {
let url = self.url(path)?;
let resp = self.http.get(url).send().await?;
let status = resp.status().as_u16();
if status == 429 {
return Err(PrecolatorError::RateLimited {
retry_after_secs: 15,
});
}
if status == 404 {
return Err(PrecolatorError::NotFound(path.to_string()));
}
if !resp.status().is_success() {
let body = resp.text().await.unwrap_or_default();
return Err(PrecolatorError::Api {
status,
message: body,
});
}
let wrapper: ApiResponse<T> = resp.json().await?;
Ok(wrapper.data)
}
pub async fn health(&self) -> Result<HealthStatus> {
self.get("/api/v1/health").await
}
pub async fn health_detailed(&self) -> Result<DetailedHealth> {
self.get("/api/v1/health/detailed").await
}
pub async fn get_markets(&self) -> Result<Vec<Market>> {
self.get("/api/v1/markets").await
}
pub async fn get_market(&self, id: &str) -> Result<Market> {
self.get(&format!("/api/v1/markets/{id}")).await
}
pub async fn get_market_stats(&self, id: &str) -> Result<MarketStats> {
self.get(&format!("/api/v1/markets/{id}/stats")).await
}
pub async fn get_trades(&self) -> Result<Vec<Trade>> {
self.get("/api/v1/trades").await
}
pub async fn get_trade(&self, trade_id: &str) -> Result<Trade> {
self.get(&format!("/api/v1/trades/{trade_id}")).await
}
pub async fn get_user_trades(&self, wallet: &str) -> Result<Vec<Trade>> {
self.get(&format!("/api/v1/trades/user/{wallet}")).await
}
pub async fn get_portfolio(&self, wallet: &str) -> Result<Portfolio> {
self.get(&format!("/api/v1/portfolio/{wallet}")).await
}
pub async fn get_portfolio_history(&self, wallet: &str) -> Result<PortfolioHistory> {
self.get(&format!("/api/v1/portfolio/{wallet}/history"))
.await
}
pub async fn get_portfolio_stats(&self, wallet: &str) -> Result<PortfolioStats> {
self.get(&format!("/api/v1/portfolio/{wallet}/stats"))
.await
}
pub async fn get_leaderboard(&self) -> Result<Vec<LeaderboardEntry>> {
self.get("/api/v1/leaderboard").await
}
pub async fn get_leaderboard_by_timeframe(
&self,
timeframe: &str,
) -> Result<Vec<LeaderboardEntry>> {
self.get(&format!("/api/v1/leaderboard/{timeframe}"))
.await
}
pub async fn get_tokens(&self) -> Result<Vec<Token>> {
self.get("/api/v1/tokens").await
}
pub async fn get_token(&self, symbol: &str) -> Result<Token> {
self.get(&format!("/api/v1/tokens/{symbol}")).await
}
pub async fn get_platform_stats(&self) -> Result<PlatformStats> {
self.get("/api/v1/stats").await
}
pub async fn get_market_overview(&self) -> Result<MarketOverview> {
self.get("/api/v1/stats/markets/overview").await
}
pub async fn get_exchange_metrics(&self) -> Result<ExchangeMetrics> {
self.get("/api/v1/stats/exchange/metrics").await
}
}