fred_rs/series/search/
mod.rs

1//! Get economic data series that match keywords
2//! 
3//! [https://research.stlouisfed.org/docs/api/fred/series_search.html](https://research.stlouisfed.org/docs/api/fred/series_search.html)
4//! 
5//! ```
6//! use fred_rs::client::FredClient;
7//! use fred_rs::series::search::{Builder, OrderBy, SortOrder};
8//! use fred_rs::series::Response;
9//! 
10//! let mut c = match FredClient::new() {
11//!     Ok(c) => c,
12//!     Err(msg) => {
13//!         println!("{}", msg);
14//!         assert_eq!(2, 1);
15//!         return
16//!     },
17//! };
18//! 
19//! let mut builder = Builder::new();
20//! builder
21//!     .limit(5)
22//!     .sort_order(SortOrder::Descending)
23//!     .order_by(OrderBy::Frequency);
24//! 
25//! let resp: Response = match c.series_search("monetary index", Some(builder)) {
26//!     Ok(resp) => resp,
27//!     Err(msg) => {
28//!         println!("{}", msg);
29//!         assert_eq!(2, 1);
30//!         return
31//!     },
32//! };
33//! 
34//! for item in resp.seriess {
35//!     println!(
36//!         "{}: {} {}",
37//!         item.id,
38//!         item.title,
39//!         item.frequency,
40//!     );
41//! }
42//! ```
43
44pub mod tags;
45pub mod related_tags;
46
47// ----------------------------------------------------------------------------
48
49/// Determines the type of search to perform
50/// 
51/// [https://research.stlouisfed.org/docs/api/fred/series_search.html#search_type](https://research.stlouisfed.org/docs/api/fred/series_search.html#search_type)
52pub enum SearchType {
53    /// (Default) Search series attributes including title, units, frequency and tags
54    FullText,
55    /// Search only the series ID number
56    /// Wildcards are accepted with this option
57    SeriesId,
58}
59
60/// Determines the order of search results
61/// 
62/// [https://research.stlouisfed.org/docs/api/fred/series_search.html#order_by](https://research.stlouisfed.org/docs/api/fred/series_search.html#order_by)
63pub enum OrderBy {
64    /// Default if search type is FULL_TEXT
65    SearchRank,
66    /// Default if search type is SERIES_ID
67    SeriesId,
68    Title,
69    Units,
70    Frequency,
71    SeasonalAdjustment,
72    RealtimeStart,
73    RealtimeEnd,
74    LastUpdated,
75    ObservationStart,
76    ObservationEnd,
77    Popularity,
78    GroupPopularity,
79}
80
81/// Sort order options for the fred/series/search endpoint
82/// 
83/// [https://research.stlouisfed.org/docs/api/fred/series_search.html#sort_order](https://research.stlouisfed.org/docs/api/fred/series_search.html#sort_order)
84pub enum SortOrder {
85    /// Dates returned in ascending order (default)
86    Ascending,    
87    /// Dates returned in descending order
88    Descending,   
89}
90
91/// Apply result filter
92/// 
93/// This should be used in conjunction with the filter_value argument to filter results based on one (maybe more than one?) of the fields.
94/// 
95/// [https://research.stlouisfed.org/docs/api/fred/series_search.html#filter_variable](https://research.stlouisfed.org/docs/api/fred/series_search.html#filter_variable)
96pub enum FilterVariable {
97    Frequency,
98    Units,
99    SeasonalAdjustment,
100}
101
102pub struct Builder {
103    option_string: String,
104    include_tags: String,
105    exclude_tags: String,
106}
107
108impl Builder {
109
110    /// Initializes a new series::search::Builder that can be used to add commands to an API request
111    /// 
112    /// The builder does not do validity checking of the arguments nor does it check for duplicates.
113    /// 
114    /// ```
115    /// use fred_rs::series::search::Builder;
116    /// // Create a new builder
117    /// let mut builder = Builder::new();
118    /// // add arguments to the builder
119    /// builder
120    ///     .realtime_start("1900-01-01")
121    ///     .realtime_end("2000-01-01");
122    /// ```
123    pub fn new() -> Builder {
124        Builder {
125            option_string: String::new(),
126            include_tags: String::new(),
127            exclude_tags: String::new(),
128        }
129    }
130
131    /// Returns the current arguments as a URL formatted string
132    pub(crate) fn build(mut self) -> String {
133        if self.include_tags.len() > 0 {
134            self.option_string += format!("&tag_names={}", self.include_tags).as_str()
135        }
136        if self.exclude_tags.len() > 0 {
137            self.option_string += format!("&exclude_tag_names={}", self.exclude_tags).as_str()
138        }
139        self.option_string
140    }
141
142    /// Adds the search_type argument to the request
143    /// 
144    /// # Arguments
145    /// * `stype` - search type (See SearchType enum)
146    /// [https://research.stlouisfed.org/docs/api/fred/series_search.html#search_type](https://research.stlouisfed.org/docs/api/fred/series_search.html#search_type)
147    pub fn search_type(&mut self, stype: SearchType) -> &mut Builder {
148        match stype {
149            SearchType::SeriesId => {
150                self.option_string += "&search_type=series_id";
151            },
152            _ => (), // FULL_TEXT is default
153        };
154        self
155    }
156
157    /// Adds a realtime_start argument to the builder
158    /// 
159    /// # Arguments
160    /// * `start_date` - date formatted as YYYY-MM-DD
161    /// 
162    /// [https://research.stlouisfed.org/docs/api/fred/series_search.html#realtime_start](https://research.stlouisfed.org/docs/api/fred/series_search.html#realtime_start)
163    pub fn realtime_start(&mut self, start_date: &str) -> &mut Builder {
164        self.option_string += format!("&realtime_start={}", start_date).as_str();
165        self
166    }
167
168    /// Adds a realtime_end argument to the builder
169    /// 
170    /// # Arguments
171    /// * `end_date` - date formatted as YYYY-MM-DD
172    /// 
173    /// [https://research.stlouisfed.org/docs/api/fred/series_search.html#realtime_end](https://research.stlouisfed.org/docs/api/fred/series_search.html#realtime_end)
174    pub fn realtime_end(&mut self, end_date: &str) -> &mut Builder {
175        self.option_string += format!("&realtime_end={}", end_date).as_str();
176        self
177    }
178
179    /// Adds a limit argument to the builder
180    /// 
181    /// The limit argument specifies a maximum number of observations to return.
182    /// 
183    /// # Arguments
184    /// * `num_results` - Maximum number of results to return
185    /// 
186    /// [https://research.stlouisfed.org/docs/api/fred/series_search.html#limit](https://research.stlouisfed.org/docs/api/fred/series_search.html#limit)
187    pub fn limit(&mut self, num_results: usize) -> &mut Builder {
188        let num_results = if num_results > 1000 { // max value is 1000
189            1000
190        } else {
191            num_results
192        };
193        self.option_string += format!("&limit={}", num_results).as_str();
194        self
195    }
196
197    /// Adds an offset argument to the builder
198    /// 
199    /// Adding an offset shifts the starting result number.  For example, if limit is 5 and offset is 0 then results 1-5 will be returned, but if offset was 5 then results 6-10 would be returned.
200    /// 
201    /// # Arguments
202    /// * `ofs` - the offset amount
203    /// 
204    /// [https://research.stlouisfed.org/docs/api/fred/series_search.html#offset](https://research.stlouisfed.org/docs/api/fred/series_search.html#offset)
205    pub fn offset(&mut self, ofs: usize) -> &mut Builder {
206        self.option_string += format!("&offset={}", ofs).as_str();
207        self
208    }
209
210    /// Adds the search_type argument to the request
211    /// 
212    /// # Arguments
213    /// * `order` - result ranking system
214    /// 
215    /// [https://research.stlouisfed.org/docs/api/fred/series_search.html#order_by](https://research.stlouisfed.org/docs/api/fred/series_search.html#order_by)
216    pub fn order_by(&mut self, order: OrderBy) -> &mut Builder {
217        match order {
218            OrderBy::SearchRank => {
219                self.option_string += "&order_by=search_rank";
220            },
221            OrderBy::SeriesId => {
222                self.option_string += "&order_by=series_id";
223            },
224            OrderBy::Title => {
225                self.option_string += "&order_by=title";
226            },
227            OrderBy::Units => {
228                self.option_string += "&order_by=units";
229            },
230            OrderBy::Frequency => {
231                self.option_string += "&order_by=frequency";
232            },
233            OrderBy::SeasonalAdjustment => {
234                self.option_string += "&order_by=seasonal_adjustment";
235            },
236            OrderBy::RealtimeStart => {
237                self.option_string += "&order_by=realtime_start";
238            },
239            OrderBy::RealtimeEnd => {
240                self.option_string += "&order_by=realtime_end";
241            },
242            OrderBy::LastUpdated => {
243                self.option_string += "&order_by=last_updated";
244            },
245            OrderBy::ObservationStart => {
246                self.option_string += "&order_by=observation_start";
247            },
248            OrderBy::ObservationEnd => {
249                self.option_string += "&order_by=observation_end";
250            },
251            OrderBy::Popularity => {
252                self.option_string += "&order_by=popularity";
253            },
254            OrderBy::GroupPopularity => {
255                self.option_string += "&order_by=group_popularity";
256            },
257        };
258        self
259    }
260
261    /// Change the sort order of the data
262    /// 
263    /// # Arguments
264    /// * `order` - Data sort order enum
265    /// 
266    /// [https://research.stlouisfed.org/docs/api/fred/series_search.html#sort_order](https://research.stlouisfed.org/docs/api/fred/series_search.html#sort_order)
267    pub fn sort_order(&mut self, order: SortOrder) -> &mut Builder {
268        match order {
269            SortOrder::Descending => {
270                self.option_string += format!("&sort_order=desc").as_str()
271            },
272            _ => () // ASC is the default so do nothing
273        }
274        self
275    }
276
277    /// Adds the filter_variable argument to the request
278    /// 
279    /// # Arguments
280    /// * `var` - the varible by which to filter
281    /// 
282    /// [https://research.stlouisfed.org/docs/api/fred/series_search.html#filter_variable](https://research.stlouisfed.org/docs/api/fred/series_search.html#filter_variable)
283    pub fn filter_variable(&mut self, var: FilterVariable) -> &mut Builder {
284        match var {
285            FilterVariable::Frequency => {
286                self.option_string += "&filter_variable=frequency";
287            },
288            FilterVariable::Units => {
289                self.option_string += "&filter_variable=units";
290            },
291            FilterVariable::SeasonalAdjustment => {
292                self.option_string += "&filter_variable=seasonal_adjustment";
293            },
294        };
295        self
296    }
297
298    /// Sets the filter value for the specified filter variable
299    /// 
300    /// Results will only include a subset of the original results that match this value for the filter_variable argument.
301    /// 
302    /// # Arguments
303    /// * `val` - the filter value
304    /// 
305    /// [https://research.stlouisfed.org/docs/api/fred/series_search.html#filter_value](https://research.stlouisfed.org/docs/api/fred/series_search.html#filter_value)
306    pub fn filter_value(&mut self, val: &str) -> &mut Builder {
307        self.option_string += format!("&filter_value={}", val).as_str();
308        self
309    }
310
311    /// Adds a tag name to include in the search
312    /// 
313    /// Results must match all included tag names.
314    /// 
315    /// # Arguments
316    /// * `tag` - tag name to add
317    /// 
318    /// [https://research.stlouisfed.org/docs/api/fred/series_search.html#tag_names](https://research.stlouisfed.org/docs/api/fred/series_search.html#tag_names)
319    pub fn tag_name(&mut self, tag: &str) -> &mut Builder {
320        if self.include_tags.len() != 0 {
321            self.include_tags.push(';');
322        } 
323        self.include_tags += tag;
324        self
325    }
326
327    /// Adds a tag name to exclude in the search
328    /// 
329    /// Results must match no excluded tag names.
330    /// 
331    /// # Arguments
332    /// * `tag` - tag name to add
333    /// 
334    /// [https://research.stlouisfed.org/docs/api/fred/series_search.html#exclude_tag_names](https://research.stlouisfed.org/docs/api/fred/series_search.html#exclude_tag_names)
335    pub fn exclude_tag(&mut self, tag: &str) -> &mut Builder {
336        if self.exclude_tags.len() != 0 {
337            self.exclude_tags.push(';');
338        } 
339        self.exclude_tags += tag;
340        self
341    }
342
343}
344
345#[cfg(test)]
346mod tests {
347    use super::*;
348    use crate::series::Response;
349    use crate::client::FredClient;
350
351    #[test]
352    fn series_search_with_options() {
353        let mut c = match FredClient::new() {
354            Ok(c) => c,
355            Err(msg) => {
356                println!("{}", msg);
357                assert_eq!(2, 1);
358                return
359            },
360        };
361
362        let mut builder = Builder::new();
363        builder
364            .limit(5)
365            .sort_order(SortOrder::Descending)
366            .order_by(OrderBy::Frequency);
367
368        let resp: Response = match c.series_search("monetary index", Some(builder)) {
369            Ok(resp) => resp,
370            Err(msg) => {
371                println!("{}", msg);
372                assert_eq!(2, 1);
373                return
374            },
375        };
376
377        for item in resp.seriess {
378            println!(
379                "{}: {} {}",
380                item.id,
381                item.title,
382                item.frequency,
383            );
384        }
385    } 
386}