1use crate::{attribute::Attribute, ItemType};
2
3use super::GetUrl;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum Filter {
26 Match(Attribute, Operator, Vec<String>),
27 Exists(Attribute, bool),
28}
29
30impl GetUrl for Filter {
31 fn get_url(&self) -> String {
32 match self {
33 Filter::Match(attribute, operation, values) => {
34 let mut url = attribute.get_url();
35 url.push_str(&operation.get_url());
36 url.push_str(&values.join(","));
37 url
38 }
39 Filter::Exists(attribute, exists) => {
40 let mut url = String::new();
41 if !exists {
42 url.push('!');
43 }
44 url.push_str(&attribute.get_url());
45 url
46 }
47 }
48 }
49}
50
51impl Filter {
52 pub(crate) fn get_item_type(&self) -> ItemType {
53 match self {
54 Filter::Match(attribute, _, _) => attribute.get_item_type(),
55 Filter::Exists(attribute, _) => attribute.get_item_type(),
56 }
57 }
58}
59
60#[derive(Debug, Copy, Clone, PartialEq, Eq)]
62pub enum Operator {
63 Eq,
65 Ne,
67 Gt,
69 Lt,
71 Gte,
73 Lte,
75}
76
77impl GetUrl for Operator {
78 fn get_url(&self) -> String {
79 match self {
80 Operator::Eq => "=",
81 Operator::Ne => "!=",
82 Operator::Gt => ">",
83 Operator::Lt => "<",
84 Operator::Gte => ">=",
85 Operator::Lte => "<=",
86 }
87 .into()
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 use crate::attribute::{Attribute, BookAttribute, CharacterAttribute, MovieAttribute};
96
97 #[test]
98 fn test_match_eq() {
99 let filter_eq = Filter::Match(
100 Attribute::Book(BookAttribute::Name),
101 Operator::Eq,
102 vec!["The Fellowship of the Ring".to_string()],
103 );
104 assert_eq!(
105 filter_eq.get_url(),
106 "name=The Fellowship of the Ring".to_string()
107 );
108 }
109
110 #[test]
111 fn test_match_ne() {
112 let filter_ne = Filter::Match(
113 Attribute::Book(BookAttribute::Name),
114 Operator::Ne,
115 vec!["The Fellowship of the Ring".to_string()],
116 );
117 assert_eq!(
118 filter_ne.get_url(),
119 "name!=The Fellowship of the Ring".to_string()
120 );
121 }
122
123 #[test]
124 fn test_exists() {
125 let filter_include = Filter::Exists(Attribute::Character(CharacterAttribute::Name), true);
126 assert_eq!(filter_include.get_url(), "name".to_string());
127 }
128
129 #[test]
130 fn test_dont_exist() {
131 let filter_exclude = Filter::Exists(Attribute::Character(CharacterAttribute::Name), false);
132 assert_eq!(filter_exclude.get_url(), "!name".to_string());
133 }
134
135 #[test]
136 fn test_include_and_exclude() {
137 let filter = Filter::Match(
138 Attribute::Book(BookAttribute::Name),
139 Operator::Eq,
140 vec![
141 "The Fellowship Of The Ring".to_string(),
142 "The Two Towers".to_string(),
143 "The Return Of The King".to_string(),
144 ],
145 );
146
147 assert_eq!(
148 filter.get_url(),
149 "name=The Fellowship Of The Ring,The Two Towers,The Return Of The King".to_string()
150 );
151
152 let filter = Filter::Match(
153 Attribute::Book(BookAttribute::Name),
154 Operator::Ne,
155 vec![
156 "The Fellowship Of The Ring".to_string(),
157 "The Two Towers".to_string(),
158 "The Return Of The King".to_string(),
159 ],
160 );
161
162 assert_eq!(
163 filter.get_url(),
164 "name!=The Fellowship Of The Ring,The Two Towers,The Return Of The King".to_string()
165 );
166 }
167
168 #[test]
169 fn test_operations() {
170 let tests = vec![
171 (
172 Filter::Match(
173 Attribute::Movie(MovieAttribute::BudgetInMillions),
174 Operator::Gt,
175 vec!["10".to_string()],
176 ),
177 "budgetInMillions>10",
178 ),
179 (
180 Filter::Match(
181 Attribute::Movie(MovieAttribute::BudgetInMillions),
182 Operator::Gte,
183 vec!["10".to_string()],
184 ),
185 "budgetInMillions>=10",
186 ),
187 (
188 Filter::Match(
189 Attribute::Movie(MovieAttribute::BudgetInMillions),
190 Operator::Lt,
191 vec!["10".to_string()],
192 ),
193 "budgetInMillions<10",
194 ),
195 (
196 Filter::Match(
197 Attribute::Movie(MovieAttribute::BudgetInMillions),
198 Operator::Lte,
199 vec!["10".to_string()],
200 ),
201 "budgetInMillions<=10",
202 ),
203 ];
204
205 for (filter, expected) in tests {
206 assert_eq!(filter.get_url(), expected.to_string());
207 }
208 }
209}