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}