use crate::application::client::Client;
use crate::application::interfaces::market::MarketService;
use crate::error::AppError;
use crate::model::responses::MarketNavigationResponse;
use crate::presentation::market::{MarketData, MarketNode};
use std::future::Future;
use std::pin::Pin;
use tracing::{debug, error, warn};
pub fn build_market_hierarchy<'a>(
client: &'a Client,
node_id: Option<&'a str>,
depth: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<MarketNode>, AppError>> + 'a>> {
Box::pin(async move {
if depth > 7 {
debug!(
depth,
max_depth = 7,
"reached maximum recursion depth, stopping"
);
return Ok(Vec::new());
}
let navigation: MarketNavigationResponse = match node_id {
Some(id) => {
debug!(node_id = %id, "getting navigation node");
match client.get_market_navigation_node(id).await {
Ok(response) => {
debug!(
node_id = %id,
nodes = response.nodes.len(),
markets = response.markets.len(),
"navigation node response received"
);
response
}
Err(e) => {
error!(node_id = %id, error = ?e, "error getting navigation node");
if matches!(e, AppError::RateLimitExceeded | AppError::Unexpected(_)) {
warn!(
node_id = %id,
"rate limit or API error encountered, returning partial results"
);
return Ok(Vec::new());
}
return Err(e);
}
}
}
None => {
debug!("getting top-level navigation nodes");
match client.get_market_navigation().await {
Ok(response) => {
debug!(
nodes = response.nodes.len(),
markets = response.markets.len(),
"top-level navigation response received"
);
response
}
Err(e) => {
error!(error = ?e, "error getting top-level navigation nodes");
return Err(e);
}
}
}
};
let mut nodes = Vec::new();
let nodes_to_process = navigation.nodes;
for node in nodes_to_process.into_iter() {
match build_market_hierarchy(client, Some(&node.id), depth + 1).await {
Ok(children) => {
debug!(
node_id = %node.id,
node_name = %node.name,
count = children.len(),
"adding node to hierarchy"
);
nodes.push(MarketNode {
id: node.id.clone(),
name: node.name.clone(),
children,
markets: Vec::new(),
});
}
Err(e) => {
error!(node_id = %node.id, error = ?e, "error building hierarchy for node");
if depth < 7 {
nodes.push(MarketNode {
id: node.id.clone(),
name: format!("{} (error: {})", node.name, e),
children: Vec::new(),
markets: Vec::new(),
});
}
}
}
}
let markets_to_process = navigation.markets;
for market in markets_to_process {
debug!(
epic = %market.epic,
instrument_name = %market.instrument_name,
"adding market to hierarchy"
);
nodes.push(MarketNode {
id: market.epic.clone(),
name: market.instrument_name.clone(),
children: Vec::new(),
markets: vec![market],
});
}
Ok(nodes)
})
}
#[must_use]
pub fn extract_markets_from_hierarchy(nodes: &[MarketNode]) -> Vec<&MarketData> {
let mut all_markets = Vec::with_capacity(count_markets(nodes));
collect_markets(nodes, &mut all_markets);
all_markets
}
fn count_markets(nodes: &[MarketNode]) -> usize {
nodes
.iter()
.map(|node| node.markets.len() + count_markets(&node.children))
.sum()
}
fn collect_markets<'a>(nodes: &'a [MarketNode], out: &mut Vec<&'a MarketData>) {
for node in nodes {
out.extend(node.markets.iter());
if !node.children.is_empty() {
collect_markets(&node.children, out);
}
}
}