use http::Method;
use serde::Deserialize;
use tracing::instrument;
use crate::Result;
use crate::models::common::Epic;
use super::MarketsApi;
use super::models::{MarketDetailFilter, MarketDetails, MarketSummary, NavigationNode};
impl MarketsApi<'_> {
#[instrument(skip_all, fields(search_term = %search_term))]
pub async fn search(&self, search_term: &str) -> Result<Vec<MarketSummary>> {
#[derive(Deserialize)]
struct Envelope {
markets: Vec<MarketSummary>,
}
let encoded = percent_encode(search_term);
let path = format!("markets?searchTerm={encoded}");
let envelope: Envelope = self
.client
.transport
.request(
Method::GET,
&path,
Some(1),
None::<&()>,
&self.client.session,
)
.await?;
Ok(envelope.markets)
}
#[instrument(skip_all, fields(epic_count = epics.len(), filter = ?filter))]
pub async fn get_many(
&self,
epics: &[Epic],
filter: MarketDetailFilter,
) -> Result<Vec<MarketDetails>> {
if epics.is_empty() {
return Err(crate::Error::InvalidInput(
"epics list must not be empty".into(),
));
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct Envelope {
market_details: Vec<MarketDetails>,
}
let epic_list = epics.iter().map(Epic::as_str).collect::<Vec<_>>().join(",");
let filter_str = filter.as_query_str();
let path = format!("markets?epics={epic_list}&filter={filter_str}");
let envelope: Envelope = self
.client
.transport
.request(
Method::GET,
&path,
Some(2),
None::<&()>,
&self.client.session,
)
.await?;
Ok(envelope.market_details)
}
#[instrument(skip_all, fields(epic = %epic))]
pub async fn get(&self, epic: &Epic) -> Result<MarketDetails> {
let path = format!("markets/{epic}");
self.client
.transport
.request(
Method::GET,
&path,
Some(3),
None::<&()>,
&self.client.session,
)
.await
}
#[instrument(skip_all)]
pub async fn navigation(&self) -> Result<NavigationNode> {
self.client
.transport
.request(
Method::GET,
"marketnavigation",
Some(1),
None::<&()>,
&self.client.session,
)
.await
}
#[instrument(skip_all, fields(node_id = %node_id))]
pub async fn navigation_node(&self, node_id: &str) -> Result<NavigationNode> {
let path = format!("marketnavigation/{node_id}");
self.client
.transport
.request(
Method::GET,
&path,
Some(1),
None::<&()>,
&self.client.session,
)
.await
}
}
fn percent_encode(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for b in s.bytes() {
match b {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
out.push(b as char);
}
b' ' => out.push('+'),
other => {
use std::fmt::Write as _;
let _ = write!(out, "%{other:02X}");
}
}
}
out
}