Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(v0.10.0): Kalshi type state pattern #13

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 2 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions kalshi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "kalshi"
version = "0.9.0"
version = "0.10.0"
authors = ["David Petre <[email protected]>"]
edition = "2021"
rust-version = "1.72"
Expand All @@ -22,8 +22,8 @@ doctest = false
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"]}
uuid = { version = "1.5.0", features = ["v4", "fast-rng"]}
serde = { version = "1.0", features = ["derive"] }
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }

[dev-dependencies]
serde_json = "1.0.111"
40 changes: 26 additions & 14 deletions kalshi/src/auth.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::Kalshi;
use crate::kalshi_error::*;
use crate::{kalshi_error::*, LoggedIn, LoggedOut};
use serde::{Deserialize, Serialize};

impl<'a> Kalshi {
impl<'a> Kalshi<LoggedOut> {
/// Asynchronously logs a user into the Kalshi exchange.
///
/// This method sends a POST request to the Kalshi exchange's login endpoint with the user's credentials.
Expand All @@ -13,15 +13,15 @@ impl<'a> Kalshi {
/// * `password` - A string slice representing the user's password.
///
/// # Returns
/// - `Ok(())`: Empty result indicating successful login.
/// - `Ok(Kalshi<LoggedIn>)`: A Kalshi instance with the user logged in.
/// - `Err(KalshiError)`: Error in case of a failure in the HTTP request or response parsing.
///
/// # Example
/// ```
/// kalshi_instance.login("[email protected]", "example_password").await?;
/// let kalshi_instance = kalshi_instance.login("[email protected]", "example_password").await?;
/// ```
pub async fn login(&mut self, user: &str, password: &str) -> Result<(), KalshiError> {
let login_url: &str = &format!("{}/login", self.base_url.to_string());
pub async fn login(self, user: &str, password: &str) -> Result<Kalshi<LoggedIn>, KalshiError> {
let login_url: &str = &format!("{}/login", self.base_url);

let login_payload = LoginPayload {
email: user.to_string(),
Expand All @@ -37,36 +37,48 @@ impl<'a> Kalshi {
.json()
.await?;

self.curr_token = Some(format!("Bearer {}", result.token));
self.member_id = Some(result.member_id);
let logged_in = LoggedIn {
curr_token: format!("Bearer {}", result.token),
member_id: result.member_id,
};

return Ok(());
Ok(Kalshi {
base_url: self.base_url.clone(),
client: self.client.clone(),
state: logged_in,
})
}
}

impl<'a> Kalshi<LoggedIn> {
/// Asynchronously logs a user out of the Kalshi exchange.
///
/// Sends a POST request to the Kalshi exchange's logout endpoint. This method
/// should be called to properly terminate the session initiated by `login`.
///
/// # Returns
/// - `Ok(())`: Empty result indicating successful logout.
/// - `Ok(Kalshi<LoggedOut>)`: A Kalshi instance with the user logged out.
/// - `Err(KalshiError)`: Error in case of a failure in the HTTP request.
///
/// # Examples
/// ```
/// kalshi_instance.logout().await?;
/// ```
pub async fn logout(&self) -> Result<(), KalshiError> {
let logout_url: &str = &format!("{}/logout", self.base_url.to_string());
pub async fn logout(&self) -> Result<Kalshi<LoggedOut>, KalshiError> {
let logout_url: &str = &format!("{}/logout", self.base_url);

self.client
.post(logout_url)
.header("Authorization", self.curr_token.clone().unwrap())
.header("Authorization", self.state.curr_token.clone())
.header("content-type", "application/json".to_string())
.send()
.await?;

return Ok(());
Ok(Kalshi {
base_url: self.base_url.clone(),
client: self.client.clone(),
state: LoggedOut,
})
}
}

Expand Down
11 changes: 5 additions & 6 deletions kalshi/src/exchange.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::Kalshi;
use crate::kalshi_error::*;
use serde::{Deserialize, Serialize};

impl Kalshi {
impl<State> Kalshi<State> {
/// Asynchronously retrieves the current status of the exchange.
///
/// This function makes an HTTP GET request to the Kalshi exchange status endpoint
Expand All @@ -16,7 +16,7 @@ impl Kalshi {
/// kalshi_instance.get_exchange_status().await.unwrap();
/// ```
pub async fn get_exchange_status(&self) -> Result<ExchangeStatus, KalshiError> {
let exchange_status_url: &str = &format!("{}/exchange/status", self.base_url.to_string());
let exchange_status_url: &str = &format!("{}/exchange/status", self.base_url);

let result: ExchangeStatus = self
.client
Expand All @@ -26,7 +26,7 @@ impl Kalshi {
.json()
.await?;

return Ok(result);
Ok(result)
}

/// Asynchronously retrieves the exchange's trading schedule.
Expand All @@ -42,8 +42,7 @@ impl Kalshi {
/// kalshi_instance.get_exchange_schedule().await.unwrap();
/// ```
pub async fn get_exchange_schedule(&self) -> Result<ExchangeScheduleStandard, KalshiError> {
let exchange_schedule_url: &str =
&format!("{}/exchange/schedule", self.base_url.to_string());
let exchange_schedule_url: &str = &format!("{}/exchange/schedule", self.base_url);

let result: ExchangeScheduleResponse = self
.client
Expand All @@ -52,7 +51,7 @@ impl Kalshi {
.await?
.json()
.await?;
return Ok(result.schedule);
Ok(result.schedule)
}
}

Expand Down
65 changes: 32 additions & 33 deletions kalshi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@
//!
//! Initialize the Kalshi Struct and login using your authentication details:
//! - **IMPORTANT**: A user's authentication token expires every thirty minutes, this means
//! that you'll need to call the login function every thirty minutes in order to
//! ensure that you remain authenticated with a valid token.
//! that you'll need to call the login function every thirty minutes in order to
//! ensure that you remain authenticated with a valid token.
//! - Storing user / password information in plaintext is not recommended,
//! an implementation of extracting user details from local environmental variables
//! is available [here](https://github.com/dpeachpeach/kalshi-rust/blob/main/sample_bot/src/main.rs#L12)
//! an implementation of extracting user details from local environmental variables
//! is available [here](https://github.com/dpeachpeach/kalshi-rust/blob/main/sample_bot/src/main.rs#L12)
//! ```
//! use kalshi::Kalshi;
//! use kalshi::TradingEnvironment;
Expand All @@ -49,7 +49,7 @@
//!
//! let mut kalshi_instance = Kalshi::new(TradingEnvironment::DemoMode);
//!
//! kalshi_instance.login(username, password).await?;
//! let kalshi_instance = kalshi_instance.login(username, password).await?;
//! ```
//!
//! After logging in, you can call any method present in the crate without issue.
Expand Down Expand Up @@ -123,14 +123,22 @@ mod kalshi_error;
mod market;
mod portfolio;

pub use auth::*;
pub use exchange::*;
pub use kalshi_error::*;
pub use market::*;
pub use portfolio::*;

// imports
use reqwest;
#[derive(Debug, Clone)]
pub struct LoggedOut;

#[derive(Debug, Clone)]
pub struct LoggedIn {
/// - `curr_token`: A field for storing the current authentication token.
curr_token: String,
#[allow(dead_code)]
/// - `member_id`: A field for storing the member ID.
member_id: String,
}

/// The Kalshi struct is the core of the kalshi-crate. It acts as the interface
/// between the user and the market, abstracting away the meat of requests
Expand All @@ -147,15 +155,13 @@ use reqwest;
///
///
#[derive(Debug, Clone)]
pub struct Kalshi {
pub struct Kalshi<State = LoggedOut> {
/// - `base_url`: The base URL for the API, determined by the trading environment.
base_url: String,
/// - `curr_token`: A field for storing the current authentication token.
curr_token: Option<String>,
/// - `member_id`: A field for storing the member ID.
member_id: Option<String>,
/// - `client`: The HTTP client used for making requests to the marketplace.
client: reqwest::Client,
/// - `state`: The state of the Kalshi instance, either logged in or logged out.
state: State,
}

impl Kalshi {
Expand All @@ -180,40 +186,33 @@ impl Kalshi {
/// let kalshi = Kalshi::new(TradingEnvironment::LiveMarketMode);
/// ```
///
pub fn new(trading_env: TradingEnvironment) -> Kalshi {
return Kalshi {
pub fn new(trading_env: TradingEnvironment) -> Kalshi<LoggedOut> {
Kalshi {
base_url: utils::build_base_url(trading_env).to_string(),
curr_token: None,
member_id: None,
client: reqwest::Client::new(),
};
state: LoggedOut,
}
}
}

/// Retrieves the current user authentication token, if available.
impl Kalshi<LoggedIn> {
/// Retrieves the current user authentication token.
///
/// # Returns
///
/// Returns an `Option<String>` containing the authentication token. If no token
/// is currently stored, it returns `None`.
/// Returns a string representing the current user authentication token.
///
/// # Examples
///
/// ```
/// use kalshi::{Kalshi, TradingEnvironment};
/// let kalshi = Kalshi::new(TradingEnvironment::DemoMode);
/// let token = kalshi.get_user_token();
/// if let Some(t) = token {
/// println!("Current token: {}", t);
/// } else {
/// println!("No token found");
/// }
/// let kalshi_instance = kalshi_instance.login("[email protected]", "example_password").await?;
/// let token = kalshi_instance.get_user_token();
/// println!("Current token: {}", token);
/// ```
///
pub fn get_user_token(&self) -> Option<String> {
match &self.curr_token {
Some(val) => return Some(val.clone()),
_ => return None,
}
pub fn get_user_token(&self) -> String {
self.state.curr_token.clone()
}
}

Expand Down
Loading