stac_api/
sort.rs

1use serde::{Deserialize, Serialize};
2use std::{
3    convert::Infallible,
4    fmt::{Display, Formatter, Result},
5    str::FromStr,
6};
7
8/// Fields by which to sort results.
9#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
10pub struct Sortby {
11    /// The field to sort by.
12    pub field: String,
13
14    /// The direction to sort by.
15    pub direction: Direction,
16}
17
18/// The direction of sorting.
19#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
20pub enum Direction {
21    /// Ascending
22    #[serde(rename = "asc")]
23    Ascending,
24
25    /// Descending
26    #[serde(rename = "desc")]
27    Descending,
28}
29
30impl Sortby {
31    /// Creates a new ascending sortby for the field.
32    ///
33    /// # Examples
34    ///
35    /// ```
36    /// # use stac_api::Sortby;
37    /// let sortby = Sortby::asc("id");
38    /// ```
39    pub fn asc(field: impl ToString) -> Sortby {
40        Sortby {
41            field: field.to_string(),
42            direction: Direction::Ascending,
43        }
44    }
45
46    /// Creates a new descending sortby for the field.
47    ///
48    /// # Examples
49    ///
50    /// ```
51    /// # use stac_api::Sortby;
52    /// let sortby = Sortby::desc("id");
53    /// ```
54    pub fn desc(field: impl ToString) -> Sortby {
55        Sortby {
56            field: field.to_string(),
57            direction: Direction::Descending,
58        }
59    }
60}
61
62impl FromStr for Sortby {
63    type Err = Infallible;
64
65    fn from_str(s: &str) -> std::result::Result<Self, Infallible> {
66        if let Some(s) = s.strip_prefix('+') {
67            Ok(Sortby::asc(s))
68        } else if let Some(s) = s.strip_prefix('-') {
69            Ok(Sortby::desc(s))
70        } else {
71            Ok(Sortby::asc(s))
72        }
73    }
74}
75
76impl Display for Sortby {
77    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
78        match self.direction {
79            Direction::Ascending => write!(f, "{}", self.field),
80            Direction::Descending => write!(f, "-{}", self.field),
81        }
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::Sortby;
88    use serde_json::json;
89
90    #[test]
91    fn optional_plus() {
92        assert_eq!(
93            "properties.created".parse::<Sortby>().unwrap(),
94            "+properties.created".parse().unwrap()
95        );
96    }
97
98    #[test]
99    fn descending() {
100        assert_eq!(Sortby::desc("id"), "-id".parse().unwrap());
101    }
102
103    #[test]
104    fn names() {
105        assert_eq!(
106            json!({"field": "foo", "direction": "asc"}),
107            serde_json::to_value(Sortby::asc("foo")).unwrap()
108        );
109        assert_eq!(
110            json!({"field": "foo", "direction": "desc"}),
111            serde_json::to_value(Sortby::desc("foo")).unwrap()
112        );
113    }
114}