1use super::types::EventLevel;
4use std::borrow::Cow;
5
6#[derive(Debug, Clone)]
11pub struct QueryBuilder {
12 xpath: Option<String>,
13 level: Option<EventLevel>,
14 provider: Option<String>,
15 event_id: Option<u32>,
16 reverse: bool,
17 max_results: Option<usize>,
18 include_event_data: bool,
19 parse_message: bool,
20}
21
22impl QueryBuilder {
23 pub fn new() -> Self {
25 QueryBuilder {
26 xpath: None,
27 level: None,
28 provider: None,
29 event_id: None,
30 reverse: false,
31 max_results: None,
32 include_event_data: false,
33 parse_message: false,
34 }
35 }
36
37 pub fn xpath(mut self, xpath: impl Into<Cow<'static, str>>) -> Self {
42 self.xpath = Some(xpath.into().into_owned());
43 self
44 }
45
46 pub fn level(mut self, level: EventLevel) -> Self {
48 self.level = Some(level);
49 self
50 }
51
52 pub fn provider(mut self, name: impl Into<Cow<'static, str>>) -> Self {
54 self.provider = Some(name.into().into_owned());
55 self
56 }
57
58 pub fn event_id(mut self, id: u32) -> Self {
60 self.event_id = Some(id);
61 self
62 }
63
64 pub fn reverse(mut self) -> Self {
68 self.reverse = true;
69 self
70 }
71
72 pub fn max_results(mut self, count: usize) -> Self {
74 self.max_results = Some(count);
75 self
76 }
77
78 pub fn with_event_data(mut self) -> Self {
83 self.include_event_data = true;
84 self
85 }
86
87 pub fn with_message(mut self) -> Self {
92 self.parse_message = true;
93 self
94 }
95
96 pub fn should_parse_event_data(&self) -> bool {
98 self.include_event_data
99 }
100
101 pub fn should_parse_message(&self) -> bool {
103 self.parse_message
104 }
105
106 pub fn build_xpath(&self) -> String {
111 if let Some(xpath) = &self.xpath {
112 return xpath.clone();
113 }
114
115 let mut conditions = Vec::new();
116
117 if let Some(level) = self.level {
119 conditions.push(format!("Level <= {}", level as u8));
120 }
121
122 if let Some(ref provider) = self.provider {
124 conditions.push(format!(
125 "System/Provider/@Name='{}'",
126 escape_xpath_string(provider)
127 ));
128 }
129
130 if let Some(id) = self.event_id {
132 conditions.push(format!("EventID={}", id));
133 }
134
135 if conditions.is_empty() {
137 "Event".to_string()
138 } else {
139 format!("Event/System[{}]", conditions.join(" and "))
140 }
141 }
142
143 pub fn is_reverse(&self) -> bool {
145 self.reverse
146 }
147
148 pub fn max_results_limit(&self) -> Option<usize> {
150 self.max_results
151 }
152}
153
154impl Default for QueryBuilder {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160fn escape_xpath_string(s: &str) -> String {
162 if s.contains('\'') && s.contains('"') {
165 s.replace('"', """)
168 } else if s.contains('\'') {
169 s.to_string()
171 } else {
172 s.to_string()
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn test_query_builder_empty() {
183 let builder = QueryBuilder::new();
184 assert_eq!(builder.build_xpath(), "Event");
185 }
186
187 #[test]
188 fn test_query_builder_with_level() {
189 let builder = QueryBuilder::new().level(EventLevel::Error);
190 let xpath = builder.build_xpath();
191 assert!(xpath.contains("Level <= 3"));
192 assert!(xpath.contains("Event/System"));
193 }
194
195 #[test]
196 fn test_query_builder_with_provider() {
197 let builder = QueryBuilder::new().provider("Security");
198 let xpath = builder.build_xpath();
199 assert!(xpath.contains("System/Provider/@Name='Security'"));
200 }
201
202 #[test]
203 fn test_query_builder_with_event_id() {
204 let builder = QueryBuilder::new().event_id(4688);
205 let xpath = builder.build_xpath();
206 assert!(xpath.contains("EventID=4688"));
207 }
208
209 #[test]
210 fn test_query_builder_combined() {
211 let builder = QueryBuilder::new()
212 .level(EventLevel::Warning)
213 .provider("Application")
214 .event_id(1000);
215 let xpath = builder.build_xpath();
216 assert!(xpath.contains("Level <= 4"));
217 assert!(xpath.contains("System/Provider/@Name='Application'"));
218 assert!(xpath.contains("EventID=1000"));
219 assert!(xpath.contains(" and "));
220 }
221
222 #[test]
223 fn test_query_builder_with_raw_xpath() {
224 let builder = QueryBuilder::new()
225 .xpath("Event/System[EventID=4728]")
226 .level(EventLevel::Error); let xpath = builder.build_xpath();
228 assert_eq!(xpath, "Event/System[EventID=4728]");
229 }
230
231 #[test]
232 fn test_query_builder_reverse() {
233 let builder = QueryBuilder::new().reverse();
234 assert!(builder.is_reverse());
235
236 let builder2 = QueryBuilder::new();
237 assert!(!builder2.is_reverse());
238 }
239
240 #[test]
241 fn test_query_builder_max_results() {
242 let builder = QueryBuilder::new().max_results(100);
243 assert_eq!(builder.max_results_limit(), Some(100));
244
245 let builder2 = QueryBuilder::new();
246 assert_eq!(builder2.max_results_limit(), None);
247 }
248}