marketstack/api/
dividends.rs1use std::collections::BTreeSet;
4
5use chrono::NaiveDate;
6use derive_builder::Builder;
7
8use crate::api::common::SortOrder;
9use crate::api::paged::PaginationError;
10use crate::api::{endpoint_prelude::*, ApiError};
11
12#[derive(Debug, Builder, Clone)]
14#[builder(setter(strip_option))]
15pub struct Dividends<'a> {
16 #[builder(setter(name = "_symbols"), default)]
18 symbols: BTreeSet<Cow<'a, str>>,
19 #[builder(default)]
21 sort: Option<SortOrder>,
22 #[builder(default)]
24 date_from: Option<NaiveDate>,
25 #[builder(default)]
27 date_to: Option<NaiveDate>,
28 #[builder(setter(name = "_limit"), default)]
30 limit: Option<PageLimit>,
31 #[builder(default)]
33 offset: Option<u64>,
34}
35
36impl<'a> Dividends<'a> {
37 pub fn builder() -> DividendsBuilder<'a> {
39 DividendsBuilder::default()
40 }
41}
42
43impl<'a> DividendsBuilder<'a> {
44 pub fn symbol(&mut self, symbol: &'a str) -> &mut Self {
50 self.symbols
51 .get_or_insert_with(BTreeSet::new)
52 .insert(symbol.into());
53 self
54 }
55
56 pub fn symbols<I, V>(&mut self, iter: I) -> &mut Self
58 where
59 I: Iterator<Item = V>,
60 V: Into<Cow<'a, str>>,
61 {
62 self.symbols
63 .get_or_insert_with(BTreeSet::new)
64 .extend(iter.map(|v| v.into()));
65 self
66 }
67
68 pub fn limit(&mut self, limit: u16) -> Result<&mut Self, ApiError<PaginationError>> {
70 let new = self;
71 new.limit = Some(Some(PageLimit::new(limit)?));
72 Ok(new)
73 }
74}
75
76impl<'a> Endpoint for Dividends<'a> {
77 fn method(&self) -> Method {
78 Method::GET
79 }
80
81 fn endpoint(&self) -> Cow<'static, str> {
82 "dividends".into()
83 }
84
85 fn parameters(&self) -> QueryParams {
86 let mut params = QueryParams::default();
87
88 params
89 .extend(self.symbols.iter().map(|value| ("symbols", value)))
90 .push_opt("sort", self.sort)
91 .push_opt("date_from", self.date_from)
92 .push_opt("date_to", self.date_to)
93 .push_opt("limit", self.limit.clone())
94 .push_opt("offset", self.offset);
95
96 params
97 }
98}
99
100#[cfg(test)]
101mod tests {
102
103 use chrono::NaiveDate;
104
105 use crate::api::common::SortOrder;
106 use crate::api::dividends::Dividends;
107 use crate::api::{self, Query};
108 use crate::test::client::{ExpectedUrl, SingleTestClient};
109
110 #[test]
111 fn dividends_defaults_are_sufficient() {
112 Dividends::builder().build().unwrap();
113 }
114
115 #[test]
116 fn dividends_endpoint() {
117 let endpoint = ExpectedUrl::builder()
118 .endpoint("dividends")
119 .build()
120 .unwrap();
121 let client = SingleTestClient::new_raw(endpoint, "");
122
123 let endpoint = Dividends::builder().build().unwrap();
124 api::ignore(endpoint).query(&client).unwrap();
125 }
126
127 #[test]
128 fn dividends_symbol() {
129 let endpoint = ExpectedUrl::builder()
130 .endpoint("dividends")
131 .add_query_params(&[("symbols", "AAPL")])
132 .build()
133 .unwrap();
134 let client = SingleTestClient::new_raw(endpoint, "");
135
136 let endpoint = Dividends::builder().symbol("AAPL").build().unwrap();
137 api::ignore(endpoint).query(&client).unwrap();
138 }
139
140 #[test]
141 fn dividends_symbols() {
142 let endpoint = ExpectedUrl::builder()
143 .endpoint("dividends")
144 .add_query_params(&[("symbols", "AAPL"), ("symbols", "GOOG")])
145 .build()
146 .unwrap();
147 let client = SingleTestClient::new_raw(endpoint, "");
148
149 let endpoint = Dividends::builder()
150 .symbol("AAPL")
151 .symbols(["AAPL", "GOOG"].iter().copied())
152 .build()
153 .unwrap();
154 api::ignore(endpoint).query(&client).unwrap();
155 }
156
157 #[test]
158 fn dividends_sort() {
159 let endpoint = ExpectedUrl::builder()
160 .endpoint("dividends")
161 .add_query_params(&[("sort", "ASC")])
162 .build()
163 .unwrap();
164 let client = SingleTestClient::new_raw(endpoint, "");
165
166 let endpoint = Dividends::builder()
167 .sort(SortOrder::Ascending)
168 .build()
169 .unwrap();
170 api::ignore(endpoint).query(&client).unwrap();
171 }
172
173 #[test]
174 fn dividends_date_from() {
175 let endpoint = ExpectedUrl::builder()
176 .endpoint("dividends")
177 .add_query_params(&[("date_from", "2020-01-01")])
178 .build()
179 .unwrap();
180 let client = SingleTestClient::new_raw(endpoint, "");
181
182 let endpoint = Dividends::builder()
183 .date_from(NaiveDate::from_ymd_opt(2020, 1, 1).unwrap())
184 .build()
185 .unwrap();
186 api::ignore(endpoint).query(&client).unwrap();
187 }
188
189 #[test]
190 fn dividends_date_to() {
191 let endpoint = ExpectedUrl::builder()
192 .endpoint("dividends")
193 .add_query_params(&[("date_to", "2020-01-01")])
194 .build()
195 .unwrap();
196 let client = SingleTestClient::new_raw(endpoint, "");
197
198 let endpoint = Dividends::builder()
199 .date_to(NaiveDate::from_ymd_opt(2020, 1, 1).unwrap())
200 .build()
201 .unwrap();
202 api::ignore(endpoint).query(&client).unwrap();
203 }
204
205 #[test]
206 fn dividends_limit() {
207 let endpoint = ExpectedUrl::builder()
208 .endpoint("dividends")
209 .add_query_params(&[("limit", "50")])
210 .build()
211 .unwrap();
212 let client = SingleTestClient::new_raw(endpoint, "");
213
214 let endpoint = Dividends::builder().limit(50).unwrap().build().unwrap();
215 api::ignore(endpoint).query(&client).unwrap();
216 }
217
218 #[test]
219 fn dividends_over_limit() {
220 assert!(Dividends::builder().limit(9999).is_err());
221 }
222
223 #[test]
224 fn dividends_offset() {
225 let endpoint = ExpectedUrl::builder()
226 .endpoint("dividends")
227 .add_query_params(&[("offset", "2")])
228 .build()
229 .unwrap();
230 let client = SingleTestClient::new_raw(endpoint, "");
231
232 let endpoint = Dividends::builder().offset(2).build().unwrap();
233 api::ignore(endpoint).query(&client).unwrap();
234 }
235}