use crate::{
DEFAULT_ENDPOINT_SEARCH,
client::OpenFIGIClient,
error::Result,
model::{
enums::{
Currency, ExchCode, MarketSecDesc, MicCode, OptionType, SecurityType, SecurityType2,
StateCode,
},
request::SearchRequestBuilder,
response::SearchResponse,
},
};
use chrono::NaiveDate;
use reqwest::Method;
pub struct SingleSearchRequestBuilder {
client: OpenFIGIClient,
request_builder: SearchRequestBuilder,
}
impl SingleSearchRequestBuilder {
#[must_use]
pub fn query(mut self, query: &str) -> Self {
self.request_builder = self.request_builder.query(query);
self
}
#[must_use]
pub fn start(mut self, start: &str) -> Self {
self.request_builder = self.request_builder.start(start);
self
}
#[must_use]
pub fn exch_code(mut self, exch_code: ExchCode) -> Self {
self.request_builder = self.request_builder.exch_code(exch_code);
self
}
#[must_use]
pub fn mic_code(mut self, mic_code: MicCode) -> Self {
self.request_builder = self.request_builder.mic_code(mic_code);
self
}
#[must_use]
pub fn currency(mut self, currency: Currency) -> Self {
self.request_builder = self.request_builder.currency(currency);
self
}
#[must_use]
pub fn market_sec_des(mut self, market_sec_des: MarketSecDesc) -> Self {
self.request_builder = self.request_builder.market_sec_des(market_sec_des);
self
}
#[must_use]
pub fn security_type(mut self, security_type: SecurityType) -> Self {
self.request_builder = self.request_builder.security_type(security_type);
self
}
#[must_use]
pub fn security_type2(mut self, security_type2: SecurityType2) -> Self {
self.request_builder = self.request_builder.security_type2(security_type2);
self
}
#[must_use]
pub fn include_unlisted_equities(mut self, val: bool) -> Self {
self.request_builder = self.request_builder.include_unlisted_equities(val);
self
}
#[must_use]
pub fn option_type(mut self, option_type: OptionType) -> Self {
self.request_builder = self.request_builder.option_type(option_type);
self
}
#[must_use]
pub fn strike(mut self, strike: [Option<f64>; 2]) -> Self {
self.request_builder = self.request_builder.strike(strike);
self
}
#[must_use]
pub fn contract_size(mut self, contract_size: [Option<f64>; 2]) -> Self {
self.request_builder = self.request_builder.contract_size(contract_size);
self
}
#[must_use]
pub fn coupon(mut self, coupon: [Option<f64>; 2]) -> Self {
self.request_builder = self.request_builder.coupon(coupon);
self
}
#[must_use]
pub fn expiration(mut self, expiration: [Option<NaiveDate>; 2]) -> Self {
self.request_builder = self.request_builder.expiration(expiration);
self
}
#[must_use]
pub fn maturity(mut self, maturity: [Option<NaiveDate>; 2]) -> Self {
self.request_builder = self.request_builder.maturity(maturity);
self
}
#[must_use]
pub fn state_code(mut self, state_code: StateCode) -> Self {
self.request_builder = self.request_builder.state_code(state_code);
self
}
pub async fn send_raw(self) -> Result<reqwest::Response> {
let request = self.request_builder.build()?;
self.client
.request(DEFAULT_ENDPOINT_SEARCH, Method::POST)
.body(&request)
.send()
.await
}
pub async fn send(self) -> Result<SearchResponse> {
let client = self.client.clone();
let raw_response = self.send_raw().await?;
client.parse_response(raw_response).await
}
}
impl OpenFIGIClient {
#[must_use]
pub fn search(&self, query: &str) -> SingleSearchRequestBuilder {
SingleSearchRequestBuilder {
client: self.clone(),
request_builder: SearchRequestBuilder::new().query(query),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::client::OpenFIGIClient;
fn create_test_client() -> OpenFIGIClient {
OpenFIGIClient::new()
}
#[test]
fn test_single_search_request_builder_creation() {
let client = create_test_client();
let builder = client.search("ibm");
assert_eq!(builder.client.base_url(), client.base_url());
assert_eq!(builder.client.has_api_key(), client.has_api_key());
let request_result = builder.request_builder.build();
assert!(
request_result.is_ok(),
"Builder should create a valid search request"
);
let request = request_result.unwrap();
assert_eq!(request.query, "ibm");
}
#[test]
fn test_single_search_request_builder_chaining() {
let client = create_test_client();
let builder = client
.search("ibm")
.exch_code(ExchCode::US)
.currency(Currency::USD)
.market_sec_des(MarketSecDesc::Equity)
.security_type(SecurityType::CommonStock)
.include_unlisted_equities(true);
let request = builder
.request_builder
.build()
.expect("Should build valid search request");
assert_eq!(request.query, "ibm");
assert_eq!(request.filters.exch_code, Some(ExchCode::US));
assert_eq!(request.filters.currency, Some(Currency::USD));
assert_eq!(request.filters.market_sec_des, Some(MarketSecDesc::Equity));
assert_eq!(
request.filters.security_type,
Some(SecurityType::CommonStock)
);
assert_eq!(request.filters.include_unlisted_equities, Some(true));
assert_eq!(builder.client.base_url(), client.base_url());
}
#[test]
fn test_single_search_request_builder_option_fields() {
let client = create_test_client();
let builder = client
.search("AAPL")
.option_type(OptionType::Call)
.strike([Some(150.0), Some(200.0)])
.contract_size([Some(100.0), None])
.coupon([None, Some(5.0)]);
let request = builder
.request_builder
.build()
.expect("Should build valid search request");
assert_eq!(request.query, "AAPL");
assert_eq!(request.filters.option_type, Some(OptionType::Call));
assert_eq!(request.filters.strike, Some([Some(150.0), Some(200.0)]));
assert_eq!(request.filters.contract_size, Some([Some(100.0), None]));
assert_eq!(request.filters.coupon, Some([None, Some(5.0)]));
assert_eq!(builder.client.base_url(), client.base_url());
}
#[test]
fn test_single_search_request_builder_date_fields() {
let client = create_test_client();
let expiration_start = NaiveDate::from_ymd_opt(2024, 1, 1).unwrap();
let expiration_end = NaiveDate::from_ymd_opt(2024, 12, 31).unwrap();
let maturity_start = NaiveDate::from_ymd_opt(2025, 1, 1).unwrap();
let builder = client
.search("ibm")
.expiration([Some(expiration_start), Some(expiration_end)])
.maturity([Some(maturity_start), None])
.state_code(StateCode::CA);
let request = builder
.request_builder
.build()
.expect("Should build valid search request");
assert_eq!(request.query, "ibm");
assert_eq!(
request.filters.expiration,
Some([Some(expiration_start), Some(expiration_end)])
);
assert_eq!(request.filters.maturity, Some([Some(maturity_start), None]));
assert_eq!(request.filters.state_code, Some(StateCode::CA));
assert_eq!(builder.client.base_url(), client.base_url());
}
}