roshi 0.0.1

A Rust client for the Kalshi API
Documentation
#[macro_export]
macro_rules! define_get_and_delete_methods {
    (
        $(
            fn $method_name:ident
                ( $($param_name:ident : $param_ty:ty),* $(,)? )
                => $endpoint_const:path,
                -> $response_ty:ty,
                method: $http_method:ident
        ),* $(,)?
    ) => {
        impl HttpClient {
            $(
                pub async fn $method_name(
                    &self,
                    $($param_name : $param_ty,)*
                ) -> Result<$response_ty, reqwest::Error> {
                    // Build endpoint path string
                    let endpoint = crate::utils::format_endpoint(
                        $endpoint_const,
                        &[
                            $( (stringify!($param_name), $param_name), )*
                        ]
                    );

                    // Signing logic
                    let timestamp = crate::utils::get_timestamp_in_milliseconds().map_err(|_| {
                        reqwest::StatusCode::INTERNAL_SERVER_ERROR
                    }).unwrap_or_else(|_| 0);

                    let method = stringify!($http_method);
                    let path = $endpoint_const;
                    let string_to_sign = format!("{}{}/trade-api/v2{}", timestamp, method, path);

                    let mut signature = String::new();
                    if let Some(private_key) = &self.private_key {
                        // Sign the string with the private key
                    signature = crate::utils::sign_pss_text(private_key, &string_to_sign)
                        .map_err(|e| {
                             eprintln!("Failed to sign request: {}", e);
                            reqwest::StatusCode::INTERNAL_SERVER_ERROR
                        }).unwrap_or_else(|_| String::new());
                    }



                    // Merge headers: always use self.default_headers, then add signing headers
                    let mut merged_headers = self.default_headers.clone();
                    if let Some(access_key) = &self.access_key {
                        merged_headers.insert("KALSHI-ACCESS-KEY".to_string(), access_key.clone());
                    }
                    merged_headers.insert("KALSHI-ACCESS-TIMESTAMP".to_string(), timestamp.to_string());
                    merged_headers.insert("KALSHI-ACCESS-SIGNATURE".to_string(), signature);


                    match method {
                        "GET" => self.get(&endpoint, Some(&merged_headers)).await,
                        "DELETE" => self.delete(&endpoint, Some(&merged_headers)).await,
                        _ => unimplemented!("HTTP method not supported in macro"),
                    }
                }
            )*
        }
    };
}

#[macro_export]
macro_rules! define_post_methods {
    (
        $(
            fn $method_name:ident
                ( $($param_name:ident : $param_ty:ty),* $(,)? )
                => $endpoint_const:path,
                -> $response_ty:ty,
                method: $http_method:ident
        ),* $(,)?
    ) => {
        impl HttpClient {
            $(
                pub async fn $method_name(
                    &self,
                    $($param_name : $param_ty,)*
                ) -> Result<$response_ty, reqwest::Error> {

                    let body = {
                        // find the first argument that implements Serialize — assumes only one body param
                        #[allow(unused_mut)]
                        let mut maybe_body = None;
                        $(
                            if maybe_body.is_none() && serde_json::to_value(&$param_name).is_ok() {
                                    maybe_body = Some(&$param_name);
                            }
                        )*

                        maybe_body
                    };


                    // Signing logic
                    let timestamp = crate::utils::get_timestamp_in_milliseconds().map_err(|_| {
                        reqwest::StatusCode::INTERNAL_SERVER_ERROR
                    }).unwrap_or_else(|_| 0);

                    let method = stringify!($http_method);
                    let path = $endpoint_const;
                    let string_to_sign = format!("{}{}/trade-api/v2{}", timestamp, method, path);

                    let mut signature = String::new();
                    if let Some(private_key) = &self.private_key {
                        // Sign the string with the private key
                        signature = crate::utils::sign_pss_text(private_key, &string_to_sign)
                            .map_err(|e| {
                                eprintln!("Failed to sign request: {}", e);
                                reqwest::StatusCode::INTERNAL_SERVER_ERROR
                            }).unwrap_or_else(|_| String::new());
                    }



                    // Merge headers: always use self.default_headers, then add signing headers
                    let mut merged_headers = self.default_headers.clone();
                    if let Some(access_key) = &self.access_key {
                        merged_headers.insert("KALSHI-ACCESS-KEY".to_string(), access_key.clone());
                    }
                    merged_headers.insert("KALSHI-ACCESS-TIMESTAMP".to_string(), timestamp.to_string());
                    merged_headers.insert("KALSHI-ACCESS-SIGNATURE".to_string(), signature);

                    match method {
                        "POST" => self.post($endpoint_const, body, Some(&merged_headers)).await,
                        _ => unimplemented!("HTTP method not supported in macro"),
                    }
                }
            )*
        }
    };
}