Skip to main content

ferric_fred/
series_updates_request.rs

1use crate::{Client, Result, SeriesSearchResults, UpdatesFilter};
2use chrono::NaiveDateTime;
3
4/// A builder for a `series/updates` request, returned by
5/// [`Client::series_updates`]. Lists the series updated most recently (ordered
6/// by last-updated time), optionally narrowed to a class of series. Finish with
7/// [`send`](SeriesUpdatesRequest::send).
8///
9/// ```no_run
10/// # async fn run(client: &ferric_fred::Client) -> ferric_fred::Result<()> {
11/// use ferric_fred::UpdatesFilter;
12/// let results = client
13///     .series_updates()
14///     .filter(UpdatesFilter::Macro)
15///     .limit(20)
16///     .send()
17///     .await?;
18/// println!("{} series recently updated", results.count);
19/// # Ok(())
20/// # }
21/// ```
22#[derive(Debug, Clone)]
23#[must_use = "a SeriesUpdatesRequest does nothing until you call `.send()`"]
24pub struct SeriesUpdatesRequest<'a> {
25    client: &'a Client,
26    filter: Option<UpdatesFilter>,
27    /// FRED's `start_time`/`end_time` window — a required pair (ADR-0019), so we
28    /// hold them together to make "one set, the other missing" unrepresentable.
29    time_window: Option<(NaiveDateTime, NaiveDateTime)>,
30    limit: Option<u32>,
31    offset: Option<u32>,
32}
33
34impl<'a> SeriesUpdatesRequest<'a> {
35    pub(crate) fn new(client: &'a Client) -> Self {
36        Self {
37            client,
38            filter: None,
39            time_window: None,
40            limit: None,
41            offset: None,
42        }
43    }
44
45    /// Narrow the results to a class of series (`filter_value`); defaults to all.
46    pub fn filter(mut self, filter: UpdatesFilter) -> Self {
47        self.filter = Some(filter);
48        self
49    }
50
51    /// Limit results to series updated within a time window (`start_time` /
52    /// `end_time`), down to the minute. FRED requires these as a pair, so this
53    /// method takes both bounds at once (ADR-0019).
54    ///
55    /// The times are naive wall-clock in FRED's own timezone — they are sent as
56    /// given (formatted `%Y%m%d%H%M`), with no timezone conversion and
57    /// minute granularity.
58    pub fn time_window(mut self, start: NaiveDateTime, end: NaiveDateTime) -> Self {
59        self.time_window = Some((start, end));
60        self
61    }
62
63    /// Maximum number of results to return, `1..=1000` (`limit`).
64    pub fn limit(mut self, limit: u32) -> Self {
65        self.limit = Some(limit);
66        self
67    }
68
69    /// Number of results to skip from the start (`offset`), for paging.
70    pub fn offset(mut self, offset: u32) -> Self {
71        self.offset = Some(offset);
72        self
73    }
74
75    /// Run the request and return the recently-updated series with pagination
76    /// metadata.
77    ///
78    /// # Errors
79    ///
80    /// Returns an error if the request fails to send, FRED returns a non-success
81    /// status, or the response body cannot be deserialized.
82    pub async fn send(self) -> Result<SeriesSearchResults> {
83        self.client.execute_series_updates(&self).await
84    }
85
86    /// Serialize the set parameters to FRED query key/value pairs. `api_key` and
87    /// `file_type` are added by the client, not here.
88    pub(crate) fn query_params(&self) -> Vec<(&'static str, String)> {
89        let mut params: Vec<(&'static str, String)> = Vec::new();
90        if let Some(filter) = self.filter {
91            params.push(("filter_value", filter.query_code().to_owned()));
92        }
93        if let Some((start, end)) = self.time_window {
94            params.push(("start_time", start.format("%Y%m%d%H%M").to_string()));
95            params.push(("end_time", end.format("%Y%m%d%H%M").to_string()));
96        }
97        if let Some(limit) = self.limit {
98            params.push(("limit", limit.to_string()));
99        }
100        if let Some(offset) = self.offset {
101            params.push(("offset", offset.to_string()));
102        }
103        params
104    }
105}
106
107impl crate::paginate::sealed::Sealed for SeriesUpdatesRequest<'_> {}
108impl crate::paginate::Paginate for SeriesUpdatesRequest<'_> {
109    type Page = SeriesSearchResults;
110    const MAX_PAGE: u32 = 1000;
111    fn requested_limit(&self) -> Option<u32> {
112        self.limit
113    }
114    fn requested_offset(&self) -> Option<u32> {
115        self.offset
116    }
117    fn with_paging(self, limit: u32, offset: u32) -> Self {
118        self.limit(limit).offset(offset)
119    }
120    fn send_page(self) -> impl std::future::Future<Output = Result<Self::Page>> + Send {
121        self.send()
122    }
123}