use serde::{Deserialize, Deserializer, Serialize};
use std::collections::HashMap;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum CgError {
#[error("HTTP error: {0}")]
Http(#[from] reqwest_middleware::Error),
#[error("Request error: {0}")]
Request(#[from] reqwest::Error),
#[error("API error: {0}")]
Api(String),
}
fn deserialize_string_or_int<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
let value: Option<serde_json::Value> = Option::deserialize(deserializer)?;
match value {
None => Ok(None),
Some(serde_json::Value::Number(n)) => n
.as_i64()
.map(Some)
.ok_or_else(|| D::Error::custom("invalid number")),
Some(serde_json::Value::String(s)) => s
.parse::<i64>()
.map(Some)
.map_err(|_| D::Error::custom("invalid string number")),
Some(_) => Err(D::Error::custom("expected string or number")),
}
}
#[derive(Debug, Clone, Default)]
pub struct SimplePriceRequest {
pub ids: String,
pub vs_currencies: String,
pub include_market_cap: Option<bool>,
pub include_24hr_vol: Option<bool>,
pub include_24hr_change: Option<bool>,
pub include_last_updated_at: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoinPrice {
#[serde(flatten)]
pub prices: HashMap<String, Option<f64>>,
}
pub type SimplePriceResponse = HashMap<String, HashMap<String, Option<f64>>>;
#[derive(Debug, Clone, Default)]
pub struct CoinsListRequest {
pub include_platform: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoinListEntry {
pub id: String,
pub symbol: String,
pub name: String,
#[serde(default)]
pub platforms: Option<HashMap<String, Option<String>>>,
}
pub type CoinsListResponse = Vec<CoinListEntry>;
#[derive(Debug, Clone, Default)]
pub struct CoinsMarketsRequest {
pub vs_currency: String,
pub ids: Option<String>,
pub category: Option<String>,
pub order: Option<String>,
pub per_page: Option<u32>,
pub page: Option<u32>,
pub sparkline: Option<bool>,
pub price_change_percentage: Option<String>,
pub locale: Option<String>,
pub precision: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Roi {
pub times: Option<f64>,
pub currency: Option<String>,
pub percentage: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoinMarket {
pub id: String,
pub symbol: String,
pub name: String,
pub image: Option<String>,
pub current_price: Option<f64>,
pub market_cap: Option<f64>,
pub market_cap_rank: Option<u32>,
pub fully_diluted_valuation: Option<f64>,
pub total_volume: Option<f64>,
pub high_24h: Option<f64>,
pub low_24h: Option<f64>,
pub price_change_24h: Option<f64>,
pub price_change_percentage_24h: Option<f64>,
pub market_cap_change_24h: Option<f64>,
pub market_cap_change_percentage_24h: Option<f64>,
pub circulating_supply: Option<f64>,
pub total_supply: Option<f64>,
pub max_supply: Option<f64>,
pub ath: Option<f64>,
pub ath_change_percentage: Option<f64>,
pub ath_date: Option<String>,
pub atl: Option<f64>,
pub atl_change_percentage: Option<f64>,
pub atl_date: Option<String>,
pub roi: Option<Roi>,
pub last_updated: Option<String>,
#[serde(default)]
pub sparkline_in_7d: Option<SparklineData>,
pub price_change_percentage_1h_in_currency: Option<f64>,
pub price_change_percentage_24h_in_currency: Option<f64>,
pub price_change_percentage_7d_in_currency: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SparklineData {
pub price: Vec<f64>,
}
pub type CoinsMarketsResponse = Vec<CoinMarket>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrendingCoinItem {
pub id: String,
#[serde(default, deserialize_with = "deserialize_string_or_int")]
pub coin_id: Option<i64>,
pub name: String,
pub symbol: String,
pub market_cap_rank: Option<u32>,
pub thumb: Option<String>,
pub small: Option<String>,
pub large: Option<String>,
pub slug: Option<String>,
pub price_btc: Option<f64>,
pub score: Option<i32>,
#[serde(default)]
pub data: Option<TrendingCoinData>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrendingCoinData {
pub price: Option<f64>,
pub price_btc: Option<String>,
pub price_change_percentage_24h: Option<HashMap<String, f64>>,
pub market_cap: Option<String>,
pub market_cap_btc: Option<String>,
pub total_volume: Option<String>,
pub total_volume_btc: Option<String>,
pub sparkline: Option<String>,
pub content: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrendingCoin {
pub item: TrendingCoinItem,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrendingNft {
pub id: Option<String>,
pub name: Option<String>,
pub symbol: Option<String>,
pub thumb: Option<String>,
#[serde(default, deserialize_with = "deserialize_string_or_int")]
pub nft_contract_id: Option<i64>,
pub native_currency_symbol: Option<String>,
pub floor_price_in_native_currency: Option<f64>,
#[serde(rename = "floor_price_24h_percentage_change")]
pub floor_price_24h_percentage_change: Option<f64>,
#[serde(default)]
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrendingCategory {
#[serde(default, deserialize_with = "deserialize_string_or_int")]
pub id: Option<i64>,
pub name: Option<String>,
pub market_cap_1h_change: Option<f64>,
pub slug: Option<String>,
#[serde(default, deserialize_with = "deserialize_string_or_int")]
pub coins_count: Option<i64>,
#[serde(default)]
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrendingResponse {
pub coins: Vec<TrendingCoin>,
#[serde(default)]
pub nfts: Vec<TrendingNft>,
#[serde(default)]
pub categories: Vec<TrendingCategory>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketCapPercentage {
#[serde(flatten)]
pub percentages: HashMap<String, f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalData {
pub active_cryptocurrencies: Option<i64>,
pub upcoming_icos: Option<i64>,
pub ongoing_icos: Option<i64>,
pub ended_icos: Option<i64>,
pub markets: Option<i64>,
#[serde(default)]
pub total_market_cap: HashMap<String, f64>,
#[serde(default)]
pub total_volume: HashMap<String, f64>,
#[serde(default)]
pub market_cap_percentage: HashMap<String, f64>,
pub market_cap_change_percentage_24h_usd: Option<f64>,
pub updated_at: Option<i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalResponse {
pub data: GlobalData,
}
#[derive(Debug, Clone, Default)]
pub struct ExchangesRequest {
pub per_page: Option<u32>,
pub page: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Exchange {
pub id: String,
pub name: String,
pub year_established: Option<i32>,
pub country: Option<String>,
pub description: Option<String>,
pub url: Option<String>,
pub image: Option<String>,
pub has_trading_incentive: Option<bool>,
pub trust_score: Option<i32>,
pub trust_score_rank: Option<i32>,
pub trade_volume_24h_btc: Option<f64>,
pub trade_volume_24h_btc_normalized: Option<f64>,
}
pub type ExchangesResponse = Vec<Exchange>;
pub type SupportedVsCurrenciesResponse = Vec<String>;
#[derive(Debug, Clone, Default)]
pub struct CoinDetailRequest {
pub id: String,
pub localization: Option<bool>,
pub tickers: Option<bool>,
pub market_data: Option<bool>,
pub community_data: Option<bool>,
pub developer_data: Option<bool>,
pub sparkline: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoinDetailResponse {
pub id: String,
pub symbol: String,
pub name: String,
#[serde(default)]
pub web_slug: Option<String>,
#[serde(default)]
pub asset_platform_id: Option<String>,
#[serde(default)]
pub categories: Vec<String>,
#[serde(default)]
pub description: Option<HashMap<String, String>>,
#[serde(default)]
pub links: Option<serde_json::Value>,
#[serde(default)]
pub image: Option<CoinImage>,
#[serde(default)]
pub genesis_date: Option<String>,
#[serde(default)]
pub market_cap_rank: Option<i32>,
#[serde(default)]
pub market_data: Option<serde_json::Value>,
#[serde(default)]
pub community_data: Option<serde_json::Value>,
#[serde(default)]
pub developer_data: Option<serde_json::Value>,
#[serde(default)]
pub last_updated: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoinImage {
pub thumb: Option<String>,
pub small: Option<String>,
pub large: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct MarketChartRequest {
pub id: String,
pub vs_currency: String,
pub days: String,
pub interval: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketChartResponse {
pub prices: Vec<Vec<f64>>,
pub market_caps: Vec<Vec<f64>>,
pub total_volumes: Vec<Vec<f64>>,
}
#[derive(Debug, Clone, Default)]
pub struct CoinHistoryRequest {
pub id: String,
pub date: String,
pub localization: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoinHistoryResponse {
#[serde(default)]
pub id: Option<String>,
#[serde(default)]
pub symbol: Option<String>,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub image: Option<CoinImage>,
#[serde(default)]
pub market_data: Option<HistoricalMarketData>,
#[serde(default)]
pub community_data: Option<serde_json::Value>,
#[serde(default)]
pub developer_data: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HistoricalMarketData {
#[serde(default)]
pub current_price: Option<HashMap<String, f64>>,
#[serde(default)]
pub market_cap: Option<HashMap<String, f64>>,
#[serde(default)]
pub total_volume: Option<HashMap<String, f64>>,
}
#[derive(Debug, Clone, Default)]
pub struct OhlcRequest {
pub id: String,
pub vs_currency: String,
pub days: String,
}
pub type OhlcCandle = Vec<f64>;
pub type OhlcResponse = Vec<OhlcCandle>;