1use serde::{Deserialize, Serialize};
4
5use super::{
6 shared::traits::{
7 builder::JQuantsBuilder,
8 pagination::{HasPaginationKey, MergePage, Paginatable},
9 },
10 JQuantsApiClient, JQuantsPlanClient,
11};
12
13#[derive(Clone, Serialize)]
15pub struct TopixPricesBuilder {
16 #[serde(skip)]
17 client: JQuantsApiClient,
18
19 #[serde(skip_serializing_if = "Option::is_none")]
21 from: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
24 to: Option<String>,
25 #[serde(skip_serializing_if = "Option::is_none")]
27 date: Option<String>,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pagination_key: Option<String>,
32}
33
34impl JQuantsBuilder<TopixPricesResponse> for TopixPricesBuilder {
35 async fn send(self) -> Result<TopixPricesResponse, crate::JQuantsError> {
36 self.send_ref().await
37 }
38
39 async fn send_ref(&self) -> Result<TopixPricesResponse, crate::JQuantsError> {
40 self.client.inner.get("indices/topix", self).await
41 }
42}
43
44impl Paginatable<TopixPricesResponse> for TopixPricesBuilder {
45 fn pagination_key(mut self, pagination_key: impl Into<String>) -> Self {
46 self.pagination_key = Some(pagination_key.into());
47 self
48 }
49}
50
51impl TopixPricesBuilder {
52 pub(crate) fn new(client: JQuantsApiClient) -> Self {
54 Self {
55 client,
56 from: None,
57 to: None,
58 date: None,
59 pagination_key: None,
60 }
61 }
62
63 pub fn from(mut self, from: impl Into<String>) -> Self {
65 self.from = Some(from.into());
66 self
67 }
68
69 pub fn to(mut self, to: impl Into<String>) -> Self {
71 self.to = Some(to.into());
72 self
73 }
74
75 pub fn date(mut self, date: impl Into<String>) -> Self {
77 self.date = Some(date.into());
78 self
79 }
80}
81
82pub trait TopixPricesApi: JQuantsPlanClient {
84 fn get_topix_prices(&self) -> TopixPricesBuilder {
88 TopixPricesBuilder::new(self.get_api_client().clone())
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Deserialize)]
96pub struct TopixPricesResponse {
97 pub topix: Vec<TopixPriceItem>,
99 pub pagination_key: Option<String>,
101}
102
103impl HasPaginationKey for TopixPricesResponse {
104 fn get_pagination_key(&self) -> Option<&str> {
105 self.pagination_key.as_deref()
106 }
107}
108
109impl MergePage for TopixPricesResponse {
110 fn merge_page(
111 page: Result<Vec<Self>, crate::JQuantsError>,
112 ) -> Result<Self, crate::JQuantsError> {
113 let mut page = page?;
114 let mut merged = page.pop().unwrap();
115 for p in page {
116 merged.topix.extend(p.topix);
117 }
118 merged.pagination_key = None;
119
120 Ok(merged)
121 }
122}
123
124#[derive(Debug, Clone, PartialEq, Deserialize)]
126pub struct TopixPriceItem {
127 #[serde(rename = "Date")]
129 pub date: String,
130
131 #[serde(rename = "Open")]
133 pub open: f64,
134
135 #[serde(rename = "High")]
137 pub high: f64,
138
139 #[serde(rename = "Low")]
141 pub low: f64,
142
143 #[serde(rename = "Close")]
145 pub close: f64,
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_deserialize_topix_prices_response() {
154 let json = r#"
155 {
156 "topix": [
157 {
158 "Date": "2022-06-28",
159 "Open": 1885.52,
160 "High": 1907.38,
161 "Low": 1885.32,
162 "Close": 1907.38
163 }
164 ],
165 "pagination_key": "value1.value2."
166 }
167 "#;
168
169 let response: TopixPricesResponse = serde_json::from_str(json).unwrap();
170 let expected_response = TopixPricesResponse {
171 topix: vec![TopixPriceItem {
172 date: "2022-06-28".to_string(),
173 open: 1885.52,
174 high: 1907.38,
175 low: 1885.32,
176 close: 1907.38,
177 }],
178 pagination_key: Some("value1.value2.".to_string()),
179 };
180
181 pretty_assertions::assert_eq!(response, expected_response);
182 }
183
184 #[test]
185 fn test_deserialize_topix_prices_response_no_pagination_key() {
186 let json = r#"
187 {
188 "topix": [
189 {
190 "Date": "2022-06-28",
191 "Open": 1885.52,
192 "High": 1907.38,
193 "Low": 1885.32,
194 "Close": 1907.38
195 }
196 ]
197 }
198 "#;
199
200 let response: TopixPricesResponse = serde_json::from_str(json).unwrap();
201 let expected_response = TopixPricesResponse {
202 topix: vec![TopixPriceItem {
203 date: "2022-06-28".to_string(),
204 open: 1885.52,
205 high: 1907.38,
206 low: 1885.32,
207 close: 1907.38,
208 }],
209 pagination_key: None,
210 };
211
212 pretty_assertions::assert_eq!(response, expected_response);
213 }
214
215 #[test]
216 fn test_deserialize_topix_prices_esponse_multiple_items() {
217 let json = r#"
218 {
219 "topix": [
220 {
221 "Date": "2022-06-27",
222 "Open": 1850.50,
223 "High": 1875.75,
224 "Low": 1845.00,
225 "Close": 1860.25
226 },
227 {
228 "Date": "2022-06-28",
229 "Open": 1885.52,
230 "High": 1907.38,
231 "Low": 1885.32,
232 "Close": 1907.38
233 }
234 ],
235 "pagination_key": "value1.value2."
236 }
237 "#;
238
239 let response: TopixPricesResponse = serde_json::from_str(json).unwrap();
240 let expected_response = TopixPricesResponse {
241 topix: vec![
242 TopixPriceItem {
243 date: "2022-06-27".to_string(),
244 open: 1850.50,
245 high: 1875.75,
246 low: 1845.00,
247 close: 1860.25,
248 },
249 TopixPriceItem {
250 date: "2022-06-28".to_string(),
251 open: 1885.52,
252 high: 1907.38,
253 low: 1885.32,
254 close: 1907.38,
255 },
256 ],
257 pagination_key: Some("value1.value2.".to_string()),
258 };
259
260 pretty_assertions::assert_eq!(response, expected_response);
261 }
262
263 #[test]
264 fn test_deserialize_topix_prices_response_no_data() {
265 let json = r#"
266 {
267 "topix": []
268 }
269 "#;
270
271 let response: TopixPricesResponse = serde_json::from_str(json).unwrap();
272 let expected_response = TopixPricesResponse {
273 topix: vec![],
274 pagination_key: None,
275 };
276
277 pretty_assertions::assert_eq!(response, expected_response);
278 }
279}