roshi 0.0.1

A Rust client for the Kalshi API
Documentation
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
}