Skip to main content

ferric_fred/
series_data_request.rs

1use chrono::NaiveDate;
2
3use crate::{Client, RegionalData, Result, SeriesId};
4
5/// A builder for a GeoFRED / Maps `series/data` request, returned by
6/// [`Client::series_data`] — one regional series' values across regions, over
7/// time.
8///
9/// Only `series_id` is required; with no date set FRED returns the most recent
10/// date. Set an optional single [`date`](SeriesDataRequest::date) or a
11/// [`start_date`](SeriesDataRequest::start_date) (which returns every date from
12/// then on), and finish with [`send`](SeriesDataRequest::send).
13///
14/// ```no_run
15/// # async fn run(client: &ferric_fred::Client) -> ferric_fred::Result<()> {
16/// use ferric_fred::SeriesId;
17/// let data = client
18///     .series_data(&SeriesId::new("SMU56000000500000001"))
19///     .send()
20///     .await?;
21/// println!("{} dates", data.meta.data.len());
22/// # Ok(())
23/// # }
24/// ```
25#[derive(Debug, Clone)]
26#[must_use = "a SeriesDataRequest does nothing until you call `.send()`"]
27pub struct SeriesDataRequest<'a> {
28    client: &'a Client,
29    series_id: SeriesId,
30    date: Option<NaiveDate>,
31    start_date: Option<NaiveDate>,
32}
33
34impl<'a> SeriesDataRequest<'a> {
35    pub(crate) fn new(client: &'a Client, series_id: SeriesId) -> Self {
36        Self {
37            client,
38            series_id,
39            date: None,
40            start_date: None,
41        }
42    }
43
44    /// Return data for a single date (`date`). With neither this nor
45    /// [`start_date`](Self::start_date) set, FRED returns the most recent date.
46    pub fn date(mut self, date: NaiveDate) -> Self {
47        self.date = Some(date);
48        self
49    }
50
51    /// Return data for every date from `start_date` onward.
52    pub fn start_date(mut self, start_date: NaiveDate) -> Self {
53        self.start_date = Some(start_date);
54        self
55    }
56
57    /// Run the request and return the regional data.
58    ///
59    /// # Errors
60    ///
61    /// Returns an error if the request fails to send, FRED returns a non-success
62    /// status, or the response body cannot be deserialized.
63    pub async fn send(self) -> Result<RegionalData> {
64        self.client.execute_series_data(&self).await
65    }
66
67    /// Serialize the set parameters to FRED query key/value pairs. `api_key` and
68    /// `file_type` are added by the client, not here.
69    pub(crate) fn query_params(&self) -> Vec<(&'static str, String)> {
70        let mut params: Vec<(&'static str, String)> = Vec::new();
71        params.push(("series_id", self.series_id.as_str().to_owned()));
72        if let Some(date) = self.date {
73            params.push(("date", date.to_string()));
74        }
75        if let Some(start_date) = self.start_date {
76            params.push(("start_date", start_date.to_string()));
77        }
78        params
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    fn test_client() -> Client {
87        Client::new("test-key").expect("client builds")
88    }
89
90    #[test]
91    fn defaults_send_only_the_series_id() {
92        let client = test_client();
93        let request = client.series_data(&SeriesId::new("SMU56000000500000001"));
94        assert_eq!(
95            request.query_params(),
96            vec![("series_id", "SMU56000000500000001".to_owned())]
97        );
98    }
99
100    #[test]
101    fn date_and_start_date_serialize() {
102        let client = test_client();
103        let request = client
104            .series_data(&SeriesId::new("SMU56000000500000001"))
105            .date(NaiveDate::from_ymd_opt(2013, 1, 1).unwrap())
106            .start_date(NaiveDate::from_ymd_opt(2010, 1, 1).unwrap());
107        let params = request.query_params();
108        for expected in [
109            ("series_id", "SMU56000000500000001"),
110            ("date", "2013-01-01"),
111            ("start_date", "2010-01-01"),
112        ] {
113            assert!(
114                params.contains(&(expected.0, expected.1.to_owned())),
115                "missing {expected:?} in {params:?}"
116            );
117        }
118    }
119}