1use serde::{Deserialize, Serialize};
2use std::{
3 convert::Infallible,
4 fmt::{Display, Formatter, Result},
5 str::FromStr,
6};
7
8#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
10pub struct Sortby {
11 pub field: String,
13
14 pub direction: Direction,
16}
17
18#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
20pub enum Direction {
21 #[serde(rename = "asc")]
23 Ascending,
24
25 #[serde(rename = "desc")]
27 Descending,
28}
29
30impl Sortby {
31 pub fn asc(field: impl ToString) -> Sortby {
40 Sortby {
41 field: field.to_string(),
42 direction: Direction::Ascending,
43 }
44 }
45
46 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}