1use serde::{Deserialize, Serialize};
2use std::{
3 convert::Infallible,
4 fmt::{Display, Formatter},
5 str::FromStr,
6};
7
8#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq)]
17pub struct Fields {
18 #[serde(skip_serializing_if = "Vec::is_empty")]
20 pub include: Vec<String>,
21
22 #[serde(skip_serializing_if = "Vec::is_empty")]
24 pub exclude: Vec<String>,
25}
26
27impl FromStr for Fields {
28 type Err = Infallible;
29
30 fn from_str(s: &str) -> Result<Self, Self::Err> {
31 let mut include = Vec::new();
32 let mut exclude = Vec::new();
33 for field in s.split(',').filter(|s| !s.is_empty()) {
34 if let Some(field) = field.strip_prefix('-') {
35 exclude.push(field.to_string());
36 } else if let Some(field) = field.strip_prefix('+') {
37 include.push(field.to_string());
38 } else {
39 include.push(field.to_string());
40 }
41 }
42 Ok(Fields { include, exclude })
43 }
44}
45
46impl Display for Fields {
47 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48 let mut fields = Vec::new();
49 for include in &self.include {
50 fields.push(include.to_string());
51 }
52 for exclude in &self.exclude {
53 fields.push(format!("-{}", exclude));
54 }
55 write!(f, "{}", fields.join(","))
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::Fields;
62
63 #[test]
64 fn empty() {
65 assert_eq!(Fields::default(), "".parse().unwrap());
66 }
67
68 #[test]
69 fn plus() {
70 assert_eq!(
71 Fields {
72 include: vec!["foo".to_string()],
73 exclude: Vec::new(),
74 },
75 "+foo".parse().unwrap()
76 );
77 }
78
79 #[test]
80 fn includes() {
81 assert_eq!(
82 Fields {
83 include: vec![
84 "id".to_string(),
85 "type".to_string(),
86 "geometry".to_string(),
87 "bbox".to_string(),
88 "properties".to_string(),
89 "links".to_string(),
90 "assets".to_string(),
91 ],
92 exclude: Vec::new()
93 },
94 "id,type,geometry,bbox,properties,links,assets"
95 .parse()
96 .unwrap()
97 );
98 assert_eq!(
99 Fields {
100 include: vec![
101 "id".to_string(),
102 "type".to_string(),
103 "geometry".to_string(),
104 "bbox".to_string(),
105 "properties".to_string(),
106 "links".to_string(),
107 "assets".to_string(),
108 ],
109 exclude: Vec::new()
110 }
111 .to_string(),
112 "id,type,geometry,bbox,properties,links,assets"
113 )
114 }
115
116 #[test]
117 fn exclude() {
118 assert_eq!(
119 Fields {
120 include: Vec::new(),
121 exclude: vec!["geometry".to_string()]
122 },
123 "-geometry".parse().unwrap()
124 );
125 assert_eq!(
126 Fields {
127 include: Vec::new(),
128 exclude: vec!["geometry".to_string()]
129 }
130 .to_string(),
131 "-geometry"
132 );
133 }
134}