ig_client/application/services/
market_service.rs1use crate::application::services::MarketService;
2use crate::{
3 application::models::market::{
4 HistoricalPricesResponse, MarketDetails, MarketNavigationResponse, MarketSearchResult,
5 },
6 config::Config,
7 error::AppError,
8 session::interface::IgSession,
9 transport::http_client::IgHttpClient,
10};
11use async_trait::async_trait;
12use reqwest::Method;
13use std::sync::Arc;
14use tracing::{debug, info};
15
16pub struct MarketServiceImpl<T: IgHttpClient> {
18 config: Arc<Config>,
19 client: Arc<T>,
20}
21
22impl<T: IgHttpClient> MarketServiceImpl<T> {
23 pub fn new(config: Arc<Config>, client: Arc<T>) -> Self {
25 Self { config, client }
26 }
27
28 pub fn get_config(&self) -> &Config {
33 &self.config
34 }
35
36 pub fn set_config(&mut self, config: Arc<Config>) {
41 self.config = config;
42 }
43}
44
45#[async_trait]
46impl<T: IgHttpClient + 'static> MarketService for MarketServiceImpl<T> {
47 async fn search_markets(
48 &self,
49 session: &IgSession,
50 search_term: &str,
51 ) -> Result<MarketSearchResult, AppError> {
52 let path = format!("markets?searchTerm={}", search_term);
53 info!("Searching markets with term: {}", search_term);
54
55 let result = self
56 .client
57 .request::<(), MarketSearchResult>(Method::GET, &path, session, None, "1")
58 .await?;
59
60 debug!("{} markets found", result.markets.len());
61 Ok(result)
62 }
63
64 async fn get_market_details(
65 &self,
66 session: &IgSession,
67 epic: &str,
68 ) -> Result<MarketDetails, AppError> {
69 let path = format!("markets/{}", epic);
70 info!("Getting market details: {}", epic);
71
72 let result = self
73 .client
74 .request::<(), MarketDetails>(Method::GET, &path, session, None, "3")
75 .await?;
76
77 debug!("Market details obtained for: {}", epic);
78 Ok(result)
79 }
80
81 async fn get_multiple_market_details(
82 &self,
83 session: &IgSession,
84 epics: &[String],
85 ) -> Result<Vec<MarketDetails>, AppError> {
86 if epics.is_empty() {
87 return Ok(Vec::new());
88 }
89
90 let epics_str = epics.join(",");
92 let path = format!("markets?epics={}", epics_str);
93
94 info!(
95 "Getting market details for {} EPICs in a batch: {}",
96 epics.len(),
97 epics_str
98 );
99
100 #[derive(serde::Deserialize)]
102 struct MarketDetailsResponse {
103 #[serde(rename = "marketDetails")]
104 market_details: Vec<MarketDetails>,
105 }
106
107 let response = self
108 .client
109 .request::<(), MarketDetailsResponse>(Method::GET, &path, session, None, "2")
110 .await?;
111
112 debug!(
113 "Market details obtained for {} EPICs",
114 response.market_details.len()
115 );
116 Ok(response.market_details)
117 }
118
119 async fn get_historical_prices(
120 &self,
121 session: &IgSession,
122 epic: &str,
123 resolution: &str,
124 from: &str,
125 to: &str,
126 ) -> Result<HistoricalPricesResponse, AppError> {
127 let path = format!(
128 "prices/{}?resolution={}&from={}&to={}",
129 epic, resolution, from, to
130 );
131 info!("Getting historical prices for: {}", epic);
132
133 let result = self
134 .client
135 .request::<(), HistoricalPricesResponse>(Method::GET, &path, session, None, "3")
136 .await?;
137
138 debug!("Historical prices obtained for: {}", epic);
139 Ok(result)
140 }
141
142 async fn get_market_navigation(
143 &self,
144 session: &IgSession,
145 ) -> Result<MarketNavigationResponse, AppError> {
146 let path = "marketnavigation";
147 info!("Getting top-level market navigation nodes");
148
149 let result = self
150 .client
151 .request::<(), MarketNavigationResponse>(Method::GET, path, session, None, "1")
152 .await?;
153
154 debug!("{} navigation nodes found", result.nodes.len());
155 debug!("{} markets found at root level", result.markets.len());
156 Ok(result)
157 }
158
159 async fn get_market_navigation_node(
160 &self,
161 session: &IgSession,
162 node_id: &str,
163 ) -> Result<MarketNavigationResponse, AppError> {
164 let path = format!("marketnavigation/{}", node_id);
165 info!("Getting market navigation node: {}", node_id);
166
167 let result = self
168 .client
169 .request::<(), MarketNavigationResponse>(Method::GET, &path, session, None, "1")
170 .await?;
171
172 debug!("{} child nodes found", result.nodes.len());
173 debug!("{} markets found in node {}", result.markets.len(), node_id);
174 Ok(result)
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use crate::config::Config;
182 use crate::transport::http_client::IgHttpClientImpl;
183 use std::sync::Arc;
184
185 #[test]
186 fn test_get_and_set_config() {
187 let config = Arc::new(Config::new());
188 let client = Arc::new(IgHttpClientImpl::new(config.clone()));
189 let mut service = MarketServiceImpl::new(config.clone(), client.clone());
190 assert!(std::ptr::eq(service.get_config(), &*config));
191 let new_cfg = Arc::new(Config::default());
192 service.set_config(new_cfg.clone());
193 assert!(std::ptr::eq(service.get_config(), &*new_cfg));
194 }
195}