Skip to main content

ferric_fred/
release_dates_request.rs

1use crate::{Client, ReleaseDatesResults, Result, SortOrder};
2
3/// A builder for the release-date endpoints, returned by
4/// [`Client::releases_dates`] (`fred/releases/dates`, the publication dates of
5/// *every* release) and [`Client::release_dates`] (`fred/release/dates`, the
6/// dates of *one* release). Both share optional sort, paging, and the
7/// "include dates with no data" toggle and return [`ReleaseDatesResults`];
8/// `release_dates` additionally carries the `release_id`. Finish with
9/// [`send`](ReleaseDatesRequest::send).
10///
11/// ```no_run
12/// # async fn run(client: &ferric_fred::Client) -> ferric_fred::Result<()> {
13/// let calendar = client.releases_dates().limit(20).send().await?;
14/// println!("{} release dates", calendar.count);
15/// # Ok(())
16/// # }
17/// ```
18#[derive(Debug, Clone)]
19#[must_use = "a ReleaseDatesRequest does nothing until you call `.send()`"]
20pub struct ReleaseDatesRequest<'a> {
21    client: &'a Client,
22    /// The endpoint path, `/releases/dates` or `/release/dates`.
23    path: &'static str,
24    /// FRED's `release_id`; required by `/release/dates`, absent for
25    /// `/releases/dates`.
26    release_id: Option<String>,
27    sort_order: Option<SortOrder>,
28    limit: Option<u32>,
29    offset: Option<u32>,
30    include_dates_with_no_data: Option<bool>,
31}
32
33impl<'a> ReleaseDatesRequest<'a> {
34    pub(crate) fn new(client: &'a Client, path: &'static str) -> Self {
35        Self {
36            client,
37            path,
38            release_id: None,
39            sort_order: None,
40            limit: None,
41            offset: None,
42            include_dates_with_no_data: None,
43        }
44    }
45
46    /// Construct a request for `/release/dates` scoped to one release.
47    pub(crate) fn with_release(client: &'a Client, path: &'static str, release_id: String) -> Self {
48        Self {
49            release_id: Some(release_id),
50            ..Self::new(client, path)
51        }
52    }
53
54    /// The endpoint path this request targets (used by the client to dispatch).
55    pub(crate) fn path(&self) -> &'static str {
56        self.path
57    }
58
59    /// Sort order of the dates (`sort_order`).
60    pub fn sort_order(mut self, order: SortOrder) -> Self {
61        self.sort_order = Some(order);
62        self
63    }
64
65    /// Maximum number of results to return, `1..=10000` (`limit`).
66    pub fn limit(mut self, limit: u32) -> Self {
67        self.limit = Some(limit);
68        self
69    }
70
71    /// Number of results to skip from the start (`offset`), for paging.
72    pub fn offset(mut self, offset: u32) -> Self {
73        self.offset = Some(offset);
74        self
75    }
76
77    /// Include release dates for which no observations have yet been released
78    /// (`include_release_dates_with_no_data`) — e.g. a scheduled future date.
79    /// FRED omits these by default.
80    pub fn include_dates_with_no_data(mut self, include: bool) -> Self {
81        self.include_dates_with_no_data = Some(include);
82        self
83    }
84
85    /// Run the request and return a page of release dates with pagination
86    /// metadata.
87    ///
88    /// # Errors
89    ///
90    /// Returns an error if the request fails to send, FRED returns a non-success
91    /// status, or the response body cannot be deserialized.
92    pub async fn send(self) -> Result<ReleaseDatesResults> {
93        self.client.execute_release_dates(&self).await
94    }
95
96    /// Serialize the set parameters to FRED query key/value pairs. `api_key` and
97    /// `file_type` are added by the client, not here.
98    pub(crate) fn query_params(&self) -> Vec<(&'static str, String)> {
99        let mut params: Vec<(&'static str, String)> = Vec::new();
100        if let Some(release_id) = &self.release_id {
101            params.push(("release_id", release_id.clone()));
102        }
103        if let Some(order) = self.sort_order {
104            params.push(("sort_order", order.query_code().to_owned()));
105        }
106        if let Some(limit) = self.limit {
107            params.push(("limit", limit.to_string()));
108        }
109        if let Some(offset) = self.offset {
110            params.push(("offset", offset.to_string()));
111        }
112        if let Some(include) = self.include_dates_with_no_data {
113            params.push(("include_release_dates_with_no_data", include.to_string()));
114        }
115        params
116    }
117}
118
119impl crate::paginate::sealed::Sealed for ReleaseDatesRequest<'_> {}
120impl crate::paginate::Paginate for ReleaseDatesRequest<'_> {
121    type Page = ReleaseDatesResults;
122    const MAX_PAGE: u32 = 10_000;
123    fn requested_limit(&self) -> Option<u32> {
124        self.limit
125    }
126    fn requested_offset(&self) -> Option<u32> {
127        self.offset
128    }
129    fn with_paging(self, limit: u32, offset: u32) -> Self {
130        self.limit(limit).offset(offset)
131    }
132    fn send_page(self) -> impl std::future::Future<Output = Result<Self::Page>> + Send {
133        self.send()
134    }
135}