1use serde::{Deserialize, Serialize};
4
5use super::{
6 shared::{
7 deserialize_utils::empty_string_or_null_as_none,
8 traits::{
9 builder::JQuantsBuilder,
10 pagination::{HasPaginationKey, MergePage, Paginatable},
11 },
12 },
13 JQuantsApiClient, JQuantsPlanClient,
14};
15
16#[derive(Clone, Serialize)]
18pub struct EarningsCalendarBuilder {
19 #[serde(skip)]
20 client: JQuantsApiClient,
21
22 #[serde(skip_serializing_if = "Option::is_none")]
24 pagination_key: Option<String>,
25}
26
27impl JQuantsBuilder<EarningsCalendarResponse> for EarningsCalendarBuilder {
28 async fn send(self) -> Result<EarningsCalendarResponse, crate::JQuantsError> {
29 self.send_ref().await
30 }
31
32 async fn send_ref(&self) -> Result<EarningsCalendarResponse, crate::JQuantsError> {
33 self.client.inner.get("fins/announcement", self).await
34 }
35}
36
37impl Paginatable<EarningsCalendarResponse> for EarningsCalendarBuilder {
38 fn pagination_key(mut self, pagination_key: impl Into<String>) -> Self {
39 self.pagination_key = Some(pagination_key.into());
40 self
41 }
42}
43
44impl EarningsCalendarBuilder {
45 pub(crate) fn new(client: JQuantsApiClient) -> Self {
47 Self {
48 client,
49 pagination_key: None,
50 }
51 }
52
53 pub fn pagination_key(mut self, pagination_key: impl Into<String>) -> Self {
55 self.pagination_key = Some(pagination_key.into());
56 self
57 }
58}
59
60pub trait EarningsCalendarApi: JQuantsPlanClient {
62 fn get_earnings_calendar(&self) -> EarningsCalendarBuilder {
66 EarningsCalendarBuilder::new(self.get_api_client().clone())
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Deserialize)]
74pub struct EarningsCalendarResponse {
75 pub announcement: Vec<EarningsAnnouncementItem>,
77 pub pagination_key: Option<String>,
79}
80
81impl HasPaginationKey for EarningsCalendarResponse {
82 fn get_pagination_key(&self) -> Option<&str> {
83 self.pagination_key.as_deref()
84 }
85}
86
87impl MergePage for EarningsCalendarResponse {
88 fn merge_page(
89 page: Result<Vec<Self>, crate::JQuantsError>,
90 ) -> Result<Self, crate::JQuantsError> {
91 let mut page = page?;
92 let mut merged = page.pop().unwrap();
93 for p in page {
94 merged.announcement.extend(p.announcement);
95 }
96 merged.pagination_key = None;
97
98 Ok(merged)
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Deserialize)]
104pub struct EarningsAnnouncementItem {
105 #[serde(rename = "Date", deserialize_with = "empty_string_or_null_as_none")]
109 pub date: Option<String>,
110
111 #[serde(rename = "Code")]
113 pub code: String,
114
115 #[serde(rename = "CompanyName")]
117 pub company_name: String,
118
119 #[serde(rename = "FiscalYear")]
121 pub fiscal_year: String,
122
123 #[serde(rename = "SectorName")]
125 pub sector_name: String,
126
127 #[serde(rename = "FiscalQuarter")]
129 pub fiscal_quarter: String,
130
131 #[serde(rename = "Section")]
133 pub section: String,
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_deserialize_earnings_calendar_response() {
142 let json_data = r#"
143 {
144 "announcement": [
145 {
146 "Date": "2022-02-14",
147 "Code": "43760",
148 "CompanyName": "くふうカンパニー",
149 "FiscalYear": "9月30日",
150 "SectorName": "情報・通信業",
151 "FiscalQuarter": "第1四半期",
152 "Section": "マザーズ"
153 }
154 ],
155 "pagination_key": "value1.value2."
156 }
157 "#;
158
159 let response: EarningsCalendarResponse = serde_json::from_str(json_data).unwrap();
160
161 let expected_announcement = vec![EarningsAnnouncementItem {
162 date: Some("2022-02-14".to_string()),
163 code: "43760".to_string(),
164 company_name: "くふうカンパニー".to_string(),
165 fiscal_year: "9月30日".to_string(),
166 sector_name: "情報・通信業".to_string(),
167 fiscal_quarter: "第1四半期".to_string(),
168 section: "マザーズ".to_string(),
169 }];
170
171 let expected_response = EarningsCalendarResponse {
172 announcement: expected_announcement,
173 pagination_key: Some("value1.value2.".to_string()),
174 };
175
176 pretty_assertions::assert_eq!(response, expected_response);
177 }
178
179 #[test]
180 fn test_deserialize_earnings_calendar_response_no_pagination_key() {
181 let json_data = r#"
182 {
183 "announcement": [
184 {
185 "Date": "2022-02-14",
186 "Code": "43760",
187 "CompanyName": "くふうカンパニー",
188 "FiscalYear": "9月30日",
189 "SectorName": "情報・通信業",
190 "FiscalQuarter": "第1四半期",
191 "Section": "マザーズ"
192 }
193 ]
194 }
195 "#;
196
197 let response: EarningsCalendarResponse = serde_json::from_str(json_data).unwrap();
198
199 let expected_announcement = vec![EarningsAnnouncementItem {
200 date: Some("2022-02-14".to_string()),
201 code: "43760".to_string(),
202 company_name: "くふうカンパニー".to_string(),
203 fiscal_year: "9月30日".to_string(),
204 sector_name: "情報・通信業".to_string(),
205 fiscal_quarter: "第1四半期".to_string(),
206 section: "マザーズ".to_string(),
207 }];
208
209 let expected_response = EarningsCalendarResponse {
210 announcement: expected_announcement,
211 pagination_key: None,
212 };
213
214 pretty_assertions::assert_eq!(response, expected_response);
215 }
216
217 #[test]
218 fn test_deserialize_earnings_calendar_response_multiple_items() {
219 let json_data = r#"
220 {
221 "announcement": [
222 {
223 "Date": "2023-03-06",
224 "Code": "86970",
225 "CompanyName": "株式会社XYZ",
226 "FiscalYear": "3月31日",
227 "SectorName": "製造業",
228 "FiscalQuarter": "第4四半期",
229 "Section": "東証プライム"
230 },
231 {
232 "Date": "2023-03-07",
233 "Code": "86971",
234 "CompanyName": "株式会社ABC",
235 "FiscalYear": "9月30日",
236 "SectorName": "金融業",
237 "FiscalQuarter": "第1四半期",
238 "Section": "東証マザーズ"
239 }
240 ],
241 "pagination_key": "value3.value4."
242 }
243 "#;
244
245 let response: EarningsCalendarResponse = serde_json::from_str(json_data).unwrap();
246
247 let expected_announcement = vec![
248 EarningsAnnouncementItem {
249 date: Some("2023-03-06".to_string()),
250 code: "86970".to_string(),
251 company_name: "株式会社XYZ".to_string(),
252 fiscal_year: "3月31日".to_string(),
253 sector_name: "製造業".to_string(),
254 fiscal_quarter: "第4四半期".to_string(),
255 section: "東証プライム".to_string(),
256 },
257 EarningsAnnouncementItem {
258 date: Some("2023-03-07".to_string()),
259 code: "86971".to_string(),
260 company_name: "株式会社ABC".to_string(),
261 fiscal_year: "9月30日".to_string(),
262 sector_name: "金融業".to_string(),
263 fiscal_quarter: "第1四半期".to_string(),
264 section: "東証マザーズ".to_string(),
265 },
266 ];
267
268 let expected_response = EarningsCalendarResponse {
269 announcement: expected_announcement,
270 pagination_key: Some("value3.value4.".to_string()),
271 };
272
273 pretty_assertions::assert_eq!(response, expected_response);
274 }
275
276 #[test]
277 fn test_deserialize_earnings_calendar_response_no_data() {
278 let json_data = r#"
279 {
280 "announcement": []
281 }
282 "#;
283
284 let response: EarningsCalendarResponse = serde_json::from_str(json_data).unwrap();
285 let expected_response = EarningsCalendarResponse {
286 announcement: vec![],
287 pagination_key: None,
288 };
289
290 pretty_assertions::assert_eq!(response, expected_response);
291 }
292}