1use serde::{Deserialize, Serialize};
4
5use crate::EngineCategory;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
9pub enum SafeSearch {
10 #[default]
12 Off = 0,
13 Moderate = 1,
15 Strict = 2,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
21pub enum TimeRange {
22 Day,
23 Week,
24 Month,
25 Year,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct SearchQuery {
31 pub query: String,
33 pub categories: Vec<EngineCategory>,
35 pub language: Option<String>,
37 pub safesearch: SafeSearch,
39 pub page: u32,
41 pub time_range: Option<TimeRange>,
43 pub engines: Vec<String>,
45}
46
47impl SearchQuery {
48 pub fn new(query: impl Into<String>) -> Self {
50 Self {
51 query: query.into(),
52 categories: vec![EngineCategory::General],
53 language: None,
54 safesearch: SafeSearch::Off,
55 page: 1,
56 time_range: None,
57 engines: Vec::new(),
58 }
59 }
60
61 pub fn with_categories(mut self, categories: Vec<EngineCategory>) -> Self {
63 self.categories = categories;
64 self
65 }
66
67 pub fn with_language(mut self, language: impl Into<String>) -> Self {
69 self.language = Some(language.into());
70 self
71 }
72
73 pub fn with_safesearch(mut self, level: SafeSearch) -> Self {
75 self.safesearch = level;
76 self
77 }
78
79 pub fn with_page(mut self, page: u32) -> Self {
81 self.page = page;
82 self
83 }
84
85 pub fn with_time_range(mut self, range: TimeRange) -> Self {
87 self.time_range = Some(range);
88 self
89 }
90
91 pub fn with_engines(mut self, engines: Vec<String>) -> Self {
93 self.engines = engines;
94 self
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_search_query_new() {
104 let query = SearchQuery::new("test query");
105 assert_eq!(query.query, "test query");
106 assert_eq!(query.categories, vec![EngineCategory::General]);
107 assert_eq!(query.safesearch, SafeSearch::Off);
108 assert_eq!(query.page, 1);
109 assert!(query.language.is_none());
110 assert!(query.time_range.is_none());
111 assert!(query.engines.is_empty());
112 }
113
114 #[test]
115 fn test_search_query_with_categories() {
116 let query = SearchQuery::new("test")
117 .with_categories(vec![EngineCategory::Images, EngineCategory::Videos]);
118 assert_eq!(
119 query.categories,
120 vec![EngineCategory::Images, EngineCategory::Videos]
121 );
122 }
123
124 #[test]
125 fn test_search_query_with_language() {
126 let query = SearchQuery::new("test").with_language("en-US");
127 assert_eq!(query.language, Some("en-US".to_string()));
128 }
129
130 #[test]
131 fn test_search_query_with_safesearch() {
132 let query = SearchQuery::new("test").with_safesearch(SafeSearch::Strict);
133 assert_eq!(query.safesearch, SafeSearch::Strict);
134 }
135
136 #[test]
137 fn test_search_query_with_page() {
138 let query = SearchQuery::new("test").with_page(5);
139 assert_eq!(query.page, 5);
140 }
141
142 #[test]
143 fn test_search_query_with_time_range() {
144 let query = SearchQuery::new("test").with_time_range(TimeRange::Week);
145 assert_eq!(query.time_range, Some(TimeRange::Week));
146 }
147
148 #[test]
149 fn test_search_query_with_engines() {
150 let query =
151 SearchQuery::new("test").with_engines(vec!["ddg".to_string(), "wiki".to_string()]);
152 assert_eq!(query.engines, vec!["ddg", "wiki"]);
153 }
154
155 #[test]
156 fn test_search_query_builder_chain() {
157 let query = SearchQuery::new("rust programming")
158 .with_categories(vec![EngineCategory::General])
159 .with_language("en")
160 .with_safesearch(SafeSearch::Moderate)
161 .with_page(2)
162 .with_time_range(TimeRange::Month)
163 .with_engines(vec!["ddg".to_string()]);
164
165 assert_eq!(query.query, "rust programming");
166 assert_eq!(query.language, Some("en".to_string()));
167 assert_eq!(query.safesearch, SafeSearch::Moderate);
168 assert_eq!(query.page, 2);
169 assert_eq!(query.time_range, Some(TimeRange::Month));
170 assert_eq!(query.engines, vec!["ddg"]);
171 }
172
173 #[test]
174 fn test_safe_search_default() {
175 let default: SafeSearch = Default::default();
176 assert_eq!(default, SafeSearch::Off);
177 }
178
179 #[test]
180 fn test_safe_search_values() {
181 assert_eq!(SafeSearch::Off as u8, 0);
182 assert_eq!(SafeSearch::Moderate as u8, 1);
183 assert_eq!(SafeSearch::Strict as u8, 2);
184 }
185
186 #[test]
187 fn test_time_range_variants() {
188 let day = TimeRange::Day;
189 let week = TimeRange::Week;
190 let month = TimeRange::Month;
191 let year = TimeRange::Year;
192
193 assert_ne!(day, week);
194 assert_ne!(month, year);
195 }
196
197 #[test]
198 fn test_search_query_serialization() {
199 let query = SearchQuery::new("test");
200 let json = serde_json::to_string(&query).unwrap();
201 assert!(json.contains("\"query\":\"test\""));
202 }
203
204 #[test]
205 fn test_search_query_deserialization() {
206 let json = r#"{"query":"test","categories":["general"],"language":null,"safesearch":"Off","page":1,"time_range":null,"engines":[]}"#;
207 let query: SearchQuery = serde_json::from_str(json).unwrap();
208 assert_eq!(query.query, "test");
209 }
210}