alpaca_data/corporate_actions/
response.rs

1use crate::{Error, transport::pagination::PaginatedResponse};
2
3use super::CorporateActions;
4
5#[derive(Clone, Debug, Default, PartialEq, serde::Deserialize)]
6pub struct ListResponse {
7    pub corporate_actions: CorporateActions,
8    pub next_page_token: Option<String>,
9}
10
11impl PaginatedResponse for ListResponse {
12    fn next_page_token(&self) -> Option<&str> {
13        self.next_page_token.as_deref()
14    }
15
16    fn merge_page(&mut self, next: Self) -> Result<(), Error> {
17        self.corporate_actions.merge(next.corporate_actions);
18        self.next_page_token = next.next_page_token;
19        Ok(())
20    }
21
22    fn clear_next_page_token(&mut self) {
23        self.next_page_token = None;
24    }
25}
26
27#[cfg(test)]
28mod tests {
29    use super::ListResponse;
30    use crate::transport::pagination::PaginatedResponse;
31
32    #[test]
33    fn list_response_deserializes_official_bucketed_wrapper_shape() {
34        let response: ListResponse = serde_json::from_str(
35            r#"{"corporate_actions":{"cash_dividends":[{"id":"e2b597ca-c2cb-47af-9315-cafb8708766d","symbol":"640CVR031","cusip":"640CVR031","rate":0.055284,"special":false,"foreign":false,"process_date":"2024-08-14","ex_date":"2024-08-07","record_date":"2024-08-07","payable_date":"2024-08-15"}],"name_changes":[{"id":"564620f3-dac4-4558-a227-5c8dd6f4d82e","old_symbol":"007975113","old_cusip":"007975113","new_symbol":"22112H119","new_cusip":"22112H119","process_date":"2024-08-13"}],"contract_adjustments":[{"id":"ca-undocumented","memo":"undocumented family"}],"mystery_bucket":[{"id":"mystery-1","field":"value"}]},"next_page_token":"page-2"}"#,
36        )
37        .expect("response should deserialize");
38
39        assert_eq!(response.corporate_actions.cash_dividends.len(), 1);
40        assert_eq!(response.corporate_actions.name_changes.len(), 1);
41        assert_eq!(response.corporate_actions.contract_adjustments.len(), 1);
42        assert_eq!(
43            response
44                .corporate_actions
45                .other
46                .get("mystery_bucket")
47                .map(Vec::len),
48            Some(1)
49        );
50        assert_eq!(response.next_page_token.as_deref(), Some("page-2"));
51    }
52
53    #[test]
54    fn list_response_merge_appends_bucketed_pages_and_clears_next_page_token() {
55        let mut first: ListResponse = serde_json::from_str(
56            r#"{"corporate_actions":{"cash_dividends":[{"id":"ca-1","symbol":"AAA","cusip":"111111111","rate":0.1,"special":false,"foreign":false,"process_date":"2024-08-01","ex_date":"2024-08-01"}],"contract_adjustments":[{"id":"undoc-1"}],"mystery_bucket":[{"id":"mystery-1"}]},"next_page_token":"page-2"}"#,
57        )
58        .expect("first response should deserialize");
59        let second: ListResponse = serde_json::from_str(
60            r#"{"corporate_actions":{"cash_dividends":[{"id":"ca-2","symbol":"BBB","cusip":"222222222","rate":0.2,"special":false,"foreign":false,"process_date":"2024-08-02","ex_date":"2024-08-02"}],"name_changes":[{"id":"name-1","old_symbol":"OLD","old_cusip":"333333333","new_symbol":"NEW","new_cusip":"444444444","process_date":"2024-08-02"}],"contract_adjustments":[{"id":"undoc-2"}],"mystery_bucket":[{"id":"mystery-2"}]},"next_page_token":null}"#,
61        )
62        .expect("second response should deserialize");
63
64        first
65            .merge_page(second)
66            .expect("merge should append bucketed corporate action pages");
67        first.clear_next_page_token();
68
69        assert_eq!(first.corporate_actions.cash_dividends.len(), 2);
70        assert_eq!(first.corporate_actions.name_changes.len(), 1);
71        assert_eq!(first.corporate_actions.contract_adjustments.len(), 2);
72        assert_eq!(
73            first
74                .corporate_actions
75                .other
76                .get("mystery_bucket")
77                .map(Vec::len),
78            Some(2)
79        );
80        assert_eq!(first.next_page_token, None);
81    }
82}