use crate::constants::endpoints::market;
use crate::endpoints::portfolio;
use crate::mode::Mode;
use crate::types::*;
use reqwest::{
header::{HeaderMap, HeaderValue},
Client,
};
use rsa::RsaPrivateKey;
use serde::{de::DeserializeOwned, Serialize};
use std::{collections::HashMap, fmt::Debug};
pub struct HttpClient {
client: Client,
mode: Mode,
default_headers: HashMap<String, String>,
access_key: Option<String>,
private_key: Option<RsaPrivateKey>,
}
impl HttpClient {
pub fn new(mode: Mode, access_key: Option<String>, private_key: Option<RsaPrivateKey>) -> Self {
Self {
client: Client::new(),
mode,
default_headers: HashMap::new(),
access_key,
private_key,
}
}
pub fn base_url(&self) -> &str {
self.mode.base_url()
}
fn full_url(&self, endpoint: &str) -> String {
format!("{}{}", self.mode.base_url(), endpoint)
}
pub async fn get<T: DeserializeOwned + Send>(
&self,
endpoint: &str,
headers: Option<&HashMap<String, String>>,
) -> Result<T, reqwest::Error> {
let url = self.full_url(endpoint);
let mut req = self.client.get(url);
if let Some(hdrs) = headers {
let mut header_map = HeaderMap::new();
for (k, v) in hdrs {
let key: reqwest::header::HeaderName = k.parse().unwrap();
let value: HeaderValue = HeaderValue::from_str(v).unwrap();
header_map.insert(key, value);
}
req = req.headers(header_map);
}
let res = req.send().await?;
#[cfg(feature = "debug")]
println!(
"[ROSHI-CLIENT] GET request to {} returned status: {}",
endpoint,
res.status()
);
res.json::<T>().await
}
pub async fn delete<T: DeserializeOwned + Send>(
&self,
endpoint: &str,
headers: Option<&HashMap<String, String>>,
) -> Result<T, reqwest::Error> {
let url = self.full_url(endpoint);
let mut req = self.client.delete(url);
if let Some(hdrs) = headers {
let mut header_map = HeaderMap::new();
for (k, v) in hdrs {
let key: reqwest::header::HeaderName = k.parse().unwrap();
let value: HeaderValue = HeaderValue::from_str(v).unwrap();
header_map.insert(key, value);
}
req = req.headers(header_map);
}
let res = req.send().await?;
#[cfg(feature = "debug")]
println!(
"[ROSHI-CLIENT] DEL request to {} returned status: {}",
endpoint,
res.status()
);
res.json::<T>().await
}
pub async fn post<B: Serialize + Send + Debug, T: DeserializeOwned + Send>(
&self,
endpoint: &str,
body: B,
headers: Option<&HashMap<String, String>>,
) -> Result<T, reqwest::Error> {
let url = self.full_url(endpoint);
let mut req = self.client.post(url).json(&body);
if let Some(hdrs) = headers {
let mut header_map = HeaderMap::new();
for (k, v) in hdrs {
let key: reqwest::header::HeaderName = k.parse().unwrap();
let value: HeaderValue = HeaderValue::from_str(v).unwrap();
header_map.insert(key, value);
}
req = req.headers(header_map);
}
let res = req.send().await.unwrap();
#[cfg(feature = "debug")]
println!(
"[ROSHI-CLIENT] POST request to {} returned status: {}",
endpoint,
res.status()
);
res.json::<T>().await
}
}
define_get_and_delete_methods! {
fn get_event(event_id: &str)
=> market::GET_EVENT,
-> EventResponse,
method: GET,
fn get_markets()
=> market::GET_MARKETS,
-> MarketsResponse,
method: GET,
fn get_market(market_id: &str)
=> market::GET_MARKET,
-> Market,
method: GET,
fn get_market_orderbook(market_id: &str)
=> market::GET_MARKET_ORDERBOOK,
-> OrderbookWrapper,
method: GET,
fn get_portfolio_balance()
=> portfolio::GET_BALANCE,
-> BalanceResponse,
method: GET,
fn get_portfolio_value()
=> portfolio::GET_PORTFOLIO_VALUE,
-> PortfolioValueResponse,
method: GET,
fn get_fills()
=> portfolio::GET_FILLS,
-> FillsResponse,
method: GET,
fn get_positions()
=> portfolio::GET_POSITIONS,
-> PositionsResponse,
method: GET,
fn cancel_order(order_id: &str)
=> portfolio::CANCEL_ORDER,
-> Order,
method: DELETE,
}
define_post_methods! {
fn post_limit_order(order: OrderRequest)
=> portfolio::CREATE_ORDER,
-> Order,
method: POST
}