Skip to main content

ferric_fred/
tag.rs

1use serde::{Deserialize, Serialize};
2
3/// A FRED tag — a keyword used to classify series (e.g. `gdp`, `quarterly`,
4/// `nsa`), from the `fred/tags`, `fred/series/tags`, and related endpoints.
5///
6/// Tags are identified by [`name`](Tag::name); there is no numeric id.
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
9pub struct Tag {
10    /// The tag's name, e.g. `"gdp"`.
11    pub name: String,
12
13    /// The id of the group the tag belongs to (e.g. `"gen"` general, `"geo"`
14    /// geography, `"freq"` frequency, `"seas"` seasonal adjustment).
15    pub group_id: String,
16
17    /// Descriptive notes, when FRED provides them (may be absent or null).
18    #[serde(default)]
19    pub notes: Option<String>,
20
21    /// Relative popularity, 0–100.
22    pub popularity: u32,
23
24    /// Number of series carrying this tag.
25    pub series_count: u64,
26}
27
28/// A page of tags with pagination metadata, from the `fred/tags` and
29/// `fred/series/tags` endpoints.
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
32pub struct TagsResults {
33    /// Total number of tags available (across all pages).
34    pub count: u32,
35
36    /// The offset (number of tags skipped) for this page.
37    pub offset: u32,
38
39    /// The page-size limit that was applied.
40    pub limit: u32,
41
42    /// The tags on this page.
43    pub tags: Vec<Tag>,
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn deserializes_a_tag() {
52        let tag: Tag = serde_json::from_str(
53            r#"{"name":"gdp","group_id":"gen","notes":"Gross Domestic Product",
54                "created":"2012-02-27 10:18:19-06","popularity":80,"series_count":12345}"#,
55        )
56        .unwrap();
57        assert_eq!(tag.name, "gdp");
58        assert_eq!(tag.group_id, "gen");
59        assert_eq!(tag.notes.as_deref(), Some("Gross Domestic Product"));
60        assert_eq!(tag.popularity, 80);
61        assert_eq!(tag.series_count, 12345);
62    }
63
64    #[test]
65    fn tag_notes_may_be_null_or_absent() {
66        let with_null: Tag = serde_json::from_str(
67            r#"{"name":"nsa","group_id":"seas","notes":null,"popularity":90,"series_count":9}"#,
68        )
69        .unwrap();
70        assert!(with_null.notes.is_none());
71
72        let absent: Tag = serde_json::from_str(
73            r#"{"name":"nsa","group_id":"seas","popularity":90,"series_count":9}"#,
74        )
75        .unwrap();
76        assert!(absent.notes.is_none());
77    }
78
79    #[test]
80    fn tags_results_carry_pagination() {
81        let results: TagsResults = serde_json::from_str(
82            r#"{"count":2,"offset":0,"limit":1000,"tags":[
83                {"name":"gdp","group_id":"gen","popularity":80,"series_count":12345},
84                {"name":"quarterly","group_id":"freq","popularity":75,"series_count":6789}
85            ]}"#,
86        )
87        .unwrap();
88        assert_eq!(results.count, 2);
89        assert_eq!(results.tags.len(), 2);
90        assert_eq!(results.tags[1].name, "quarterly");
91    }
92}