Skip to main content

ferric_fred/
series_list_request.rs

1use crate::{Client, OrderBy, Result, SeriesSearchResults, SortOrder};
2
3/// A builder for the FRED endpoints that return a page of series filtered by a
4/// single facet: `category/series`, `release/series`, and `tags/series`. They
5/// share the same optional ordering and paging and all return
6/// [`SeriesSearchResults`]; they differ only in the endpoint path and the one
7/// facet parameter (`category_id` / `release_id` / `tag_names`).
8///
9/// Construct one via [`Client::category_series`], [`Client::release_series`], or
10/// [`Client::tags_series`]; finish with [`send`](SeriesListRequest::send).
11///
12/// ```no_run
13/// # async fn run(client: &ferric_fred::Client) -> ferric_fred::Result<()> {
14/// use ferric_fred::{OrderBy, ReleaseId};
15/// let results = client
16///     .release_series(ReleaseId::new(53))
17///     .order_by(OrderBy::Popularity)
18///     .limit(10)
19///     .send()
20///     .await?;
21/// println!("{} series", results.count);
22/// # Ok(())
23/// # }
24/// ```
25#[derive(Debug, Clone)]
26#[must_use = "a SeriesListRequest does nothing until you call `.send()`"]
27pub struct SeriesListRequest<'a> {
28    client: &'a Client,
29    /// The endpoint path, e.g. `/category/series`.
30    path: &'static str,
31    /// The facet filter as a `(key, value)` query pair, e.g.
32    /// `("category_id", "125")` or `("tag_names", "gdp;quarterly")`.
33    facet: (&'static str, String),
34    order_by: Option<OrderBy>,
35    sort_order: Option<SortOrder>,
36    limit: Option<u32>,
37    offset: Option<u32>,
38}
39
40impl<'a> SeriesListRequest<'a> {
41    pub(crate) fn new(
42        client: &'a Client,
43        path: &'static str,
44        facet_key: &'static str,
45        facet_value: String,
46    ) -> Self {
47        Self {
48            client,
49            path,
50            facet: (facet_key, facet_value),
51            order_by: None,
52            sort_order: None,
53            limit: None,
54            offset: None,
55        }
56    }
57
58    /// Field to order results by (`order_by`).
59    pub fn order_by(mut self, order_by: OrderBy) -> Self {
60        self.order_by = Some(order_by);
61        self
62    }
63
64    /// Sort order of the results (`sort_order`).
65    pub fn sort_order(mut self, order: SortOrder) -> Self {
66        self.sort_order = Some(order);
67        self
68    }
69
70    /// Maximum number of results to return, `1..=1000` (`limit`).
71    pub fn limit(mut self, limit: u32) -> Self {
72        self.limit = Some(limit);
73        self
74    }
75
76    /// Number of results to skip from the start (`offset`), for paging.
77    pub fn offset(mut self, offset: u32) -> Self {
78        self.offset = Some(offset);
79        self
80    }
81
82    /// Run the request and return the matching series with pagination metadata.
83    ///
84    /// # Errors
85    ///
86    /// Returns an error if the request fails to send, FRED returns a non-success
87    /// status, or the response body cannot be deserialized.
88    pub async fn send(self) -> Result<SeriesSearchResults> {
89        self.client.execute_series_list(&self).await
90    }
91
92    /// The endpoint path this request targets (used by the client to dispatch).
93    pub(crate) fn path(&self) -> &'static str {
94        self.path
95    }
96
97    /// Serialize the facet plus set options to FRED query key/value pairs.
98    /// `api_key` and `file_type` are added by the client, not here.
99    pub(crate) fn query_params(&self) -> Vec<(&'static str, String)> {
100        let mut params: Vec<(&'static str, String)> = vec![(self.facet.0, self.facet.1.clone())];
101        if let Some(order_by) = self.order_by {
102            params.push(("order_by", order_by.query_code().to_owned()));
103        }
104        if let Some(order) = self.sort_order {
105            params.push(("sort_order", order.query_code().to_owned()));
106        }
107        if let Some(limit) = self.limit {
108            params.push(("limit", limit.to_string()));
109        }
110        if let Some(offset) = self.offset {
111            params.push(("offset", offset.to_string()));
112        }
113        params
114    }
115}
116
117impl crate::paginate::sealed::Sealed for SeriesListRequest<'_> {}
118impl crate::paginate::Paginate for SeriesListRequest<'_> {
119    type Page = SeriesSearchResults;
120    const MAX_PAGE: u32 = 1000;
121    fn requested_limit(&self) -> Option<u32> {
122        self.limit
123    }
124    fn requested_offset(&self) -> Option<u32> {
125        self.offset
126    }
127    fn with_paging(self, limit: u32, offset: u32) -> Self {
128        self.limit(limit).offset(offset)
129    }
130    fn send_page(self) -> impl std::future::Future<Output = Result<Self::Page>> + Send {
131        self.send()
132    }
133}