alpaca_data/news/
response.rs

1use crate::{Error, transport::pagination::PaginatedResponse};
2
3use super::NewsItem;
4
5#[derive(Clone, Debug, Default, PartialEq, serde::Deserialize)]
6pub struct ListResponse {
7    pub news: Vec<NewsItem>,
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, mut next: Self) -> Result<(), Error> {
17        self.news.append(&mut next.news);
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_wrapper_shape() {
34        let response: ListResponse = serde_json::from_str(
35            r#"{"news":[{"id":24843171,"headline":"Apple headline","author":"Charles Gross","created_at":"2021-12-31T11:08:42Z","updated_at":"2021-12-31T11:08:43Z","summary":"Summary","content":"","url":"https://example.com/article","images":[{"size":"thumb","url":"https://example.com/image.jpg"}],"symbols":["AAPL"],"source":"benzinga"}],"next_page_token":"page-2"}"#,
36        )
37        .expect("response should deserialize");
38
39        assert_eq!(response.news.len(), 1);
40        assert_eq!(response.next_page_token.as_deref(), Some("page-2"));
41    }
42
43    #[test]
44    fn list_response_merge_appends_news_and_clears_next_page_token() {
45        let mut first: ListResponse = serde_json::from_str(
46            r#"{"news":[{"id":1,"headline":"First","author":"Author","created_at":"2026-04-01T00:00:00Z","updated_at":"2026-04-01T00:00:01Z","summary":"Summary","content":"","url":"https://example.com/1","images":[],"symbols":["AAPL"],"source":"benzinga"}],"next_page_token":"page-2"}"#,
47        )
48        .expect("first response should deserialize");
49        let second: ListResponse = serde_json::from_str(
50            r#"{"news":[{"id":2,"headline":"Second","author":"Author","created_at":"2026-04-02T00:00:00Z","updated_at":"2026-04-02T00:00:01Z","summary":"Summary","content":"","url":"https://example.com/2","images":[],"symbols":["MSFT"],"source":"benzinga"}],"next_page_token":null}"#,
51        )
52        .expect("second response should deserialize");
53
54        first
55            .merge_page(second)
56            .expect("merge should append later news page");
57        first.clear_next_page_token();
58
59        assert_eq!(first.news.len(), 2);
60        assert_eq!(first.next_page_token, None);
61    }
62}