1use std::collections::HashMap;
8
9use super::pattern::{Pattern, PatternSet};
10
11pub type PatternHandler = Box<dyn Fn(&str) -> HandlerAction + Send + Sync>;
13
14#[derive(Debug, Clone, Default)]
16pub enum HandlerAction {
17 #[default]
19 Continue,
20 Return(String),
22 Abort(String),
24 Respond(String),
26}
27
28pub struct PersistentPattern {
30 pub pattern: Pattern,
32 pub handler: PatternHandler,
34 pub enabled: bool,
36 pub priority: i32,
38}
39
40impl PersistentPattern {
41 #[must_use]
43 pub fn new(pattern: Pattern, handler: PatternHandler) -> Self {
44 Self {
45 pattern,
46 handler,
47 enabled: true,
48 priority: 0,
49 }
50 }
51
52 pub fn with_response(pattern: Pattern, response: impl Into<String>) -> Self {
54 let response = response.into();
55 Self::new(
56 pattern,
57 Box::new(move |_| HandlerAction::Respond(response.clone())),
58 )
59 }
60
61 pub fn with_abort(pattern: Pattern, message: impl Into<String>) -> Self {
63 let message = message.into();
64 Self::new(
65 pattern,
66 Box::new(move |_| HandlerAction::Abort(message.clone())),
67 )
68 }
69
70 #[must_use]
72 pub const fn with_priority(mut self, priority: i32) -> Self {
73 self.priority = priority;
74 self
75 }
76
77 pub const fn disable(&mut self) {
79 self.enabled = false;
80 }
81
82 pub const fn enable(&mut self) {
84 self.enabled = true;
85 }
86}
87
88impl std::fmt::Debug for PersistentPattern {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 f.debug_struct("PersistentPattern")
91 .field("pattern", &self.pattern)
92 .field("enabled", &self.enabled)
93 .field("priority", &self.priority)
94 .finish_non_exhaustive()
95 }
96}
97
98#[derive(Default)]
106pub struct PatternManager {
107 before_patterns: HashMap<String, PersistentPattern>,
109 after_patterns: HashMap<String, PersistentPattern>,
111 next_id: usize,
113}
114
115impl PatternManager {
116 #[must_use]
118 pub fn new() -> Self {
119 Self::default()
120 }
121
122 pub fn add_before(&mut self, pattern: PersistentPattern) -> String {
124 let id = self.generate_id("before");
125 self.before_patterns.insert(id.clone(), pattern);
126 id
127 }
128
129 pub fn add_after(&mut self, pattern: PersistentPattern) -> String {
131 let id = self.generate_id("after");
132 self.after_patterns.insert(id.clone(), pattern);
133 id
134 }
135
136 pub fn remove_before(&mut self, id: &str) -> Option<PersistentPattern> {
138 self.before_patterns.remove(id)
139 }
140
141 pub fn remove_after(&mut self, id: &str) -> Option<PersistentPattern> {
143 self.after_patterns.remove(id)
144 }
145
146 #[must_use]
148 pub fn get_before(&self, id: &str) -> Option<&PersistentPattern> {
149 self.before_patterns.get(id)
150 }
151
152 pub fn get_before_mut(&mut self, id: &str) -> Option<&mut PersistentPattern> {
154 self.before_patterns.get_mut(id)
155 }
156
157 #[must_use]
159 pub fn get_after(&self, id: &str) -> Option<&PersistentPattern> {
160 self.after_patterns.get(id)
161 }
162
163 pub fn get_after_mut(&mut self, id: &str) -> Option<&mut PersistentPattern> {
165 self.after_patterns.get_mut(id)
166 }
167
168 #[must_use]
175 pub fn check_before(&self, buffer: &str) -> Option<(String, HandlerAction, Pattern)> {
176 self.check_patterns(&self.before_patterns, buffer)
177 }
178
179 #[must_use]
185 pub fn check_after(&self, buffer: &str) -> Option<(String, HandlerAction, Pattern)> {
186 self.check_patterns(&self.after_patterns, buffer)
187 }
188
189 #[must_use]
191 pub fn before_pattern_set(&self) -> PatternSet {
192 self.patterns_to_set(&self.before_patterns)
193 }
194
195 #[must_use]
197 pub fn after_pattern_set(&self) -> PatternSet {
198 self.patterns_to_set(&self.after_patterns)
199 }
200
201 pub fn clear_before(&mut self) {
203 self.before_patterns.clear();
204 }
205
206 pub fn clear_after(&mut self) {
208 self.after_patterns.clear();
209 }
210
211 pub fn clear_all(&mut self) {
213 self.before_patterns.clear();
214 self.after_patterns.clear();
215 }
216
217 #[must_use]
219 pub fn before_count(&self) -> usize {
220 self.before_patterns.len()
221 }
222
223 #[must_use]
225 pub fn after_count(&self) -> usize {
226 self.after_patterns.len()
227 }
228
229 fn generate_id(&mut self, prefix: &str) -> String {
230 let id = format!("{prefix}_{}", self.next_id);
231 self.next_id += 1;
232 id
233 }
234
235 #[allow(clippy::unused_self)]
236 fn check_patterns(
237 &self,
238 patterns: &HashMap<String, PersistentPattern>,
239 buffer: &str,
240 ) -> Option<(String, HandlerAction, Pattern)> {
241 let mut sorted: Vec<_> = patterns.iter().filter(|(_, p)| p.enabled).collect();
243 sorted.sort_by_key(|(_, p)| p.priority);
244
245 for (id, persistent) in sorted {
246 if persistent.pattern.matches(buffer).is_some() {
247 let action = (persistent.handler)(buffer);
248 if !matches!(action, HandlerAction::Continue) {
249 return Some((id.clone(), action, persistent.pattern.clone()));
250 }
251 }
252 }
253 None
254 }
255
256 #[allow(clippy::unused_self)]
257 fn patterns_to_set(&self, patterns: &HashMap<String, PersistentPattern>) -> PatternSet {
258 let mut sorted: Vec<_> = patterns.iter().filter(|(_, p)| p.enabled).collect();
259 sorted.sort_by_key(|(_, p)| p.priority);
260
261 PatternSet::from_patterns(sorted.into_iter().map(|(_, p)| p.pattern.clone()).collect())
262 }
263}
264
265impl std::fmt::Debug for PatternManager {
266 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267 f.debug_struct("PatternManager")
268 .field("before_count", &self.before_patterns.len())
269 .field("after_count", &self.after_patterns.len())
270 .finish()
271 }
272}
273
274pub struct PatternBuilder {
276 manager: PatternManager,
277}
278
279impl PatternBuilder {
280 #[must_use]
282 pub fn new() -> Self {
283 Self {
284 manager: PatternManager::new(),
285 }
286 }
287
288 #[must_use]
290 pub fn with_password_handler(mut self, password: impl Into<String>) -> Self {
291 let password = password.into();
292 let pattern = PersistentPattern::with_response(
293 Pattern::regex(r"[Pp]assword:?\s*$").unwrap_or_else(|_| Pattern::literal("Password:")),
294 format!("{password}\n"),
295 );
296 self.manager.add_before(pattern);
297 self
298 }
299
300 #[must_use]
302 pub fn with_sudo_handler(mut self, password: impl Into<String>) -> Self {
303 let password = password.into();
304 let pattern = PersistentPattern::with_response(
305 Pattern::regex(r"\[sudo\] password")
306 .unwrap_or_else(|_| Pattern::literal("[sudo] password")),
307 format!("{password}\n"),
308 );
309 self.manager.add_before(pattern);
310 self
311 }
312
313 #[must_use]
315 pub fn with_error_pattern(mut self, pattern: Pattern, message: impl Into<String>) -> Self {
316 let persistent = PersistentPattern::with_abort(pattern, message);
317 self.manager.add_before(persistent);
318 self
319 }
320
321 #[must_use]
323 pub fn with_yes_handler(mut self) -> Self {
324 let pattern = PersistentPattern::with_response(
325 Pattern::regex(r"\(yes/no\)\??\s*$").unwrap_or_else(|_| Pattern::literal("(yes/no)")),
326 "yes\n",
327 );
328 self.manager.add_before(pattern);
329 self
330 }
331
332 #[must_use]
334 pub fn with_yn_handler(mut self) -> Self {
335 let pattern = PersistentPattern::with_response(
336 Pattern::regex(r"\[y/n\]\??\s*$").unwrap_or_else(|_| Pattern::literal("[y/n]")),
337 "y\n",
338 );
339 self.manager.add_before(pattern);
340 self
341 }
342
343 #[must_use]
345 pub fn with_continue_handler(mut self) -> Self {
346 let pattern = PersistentPattern::with_response(
347 Pattern::regex(r"Press (?:Enter|any key) to continue")
348 .unwrap_or_else(|_| Pattern::literal("Press Enter")),
349 "\n",
350 );
351 self.manager.add_before(pattern);
352 self
353 }
354
355 #[must_use]
357 pub fn build(self) -> PatternManager {
358 self.manager
359 }
360}
361
362impl Default for PatternBuilder {
363 fn default() -> Self {
364 Self::new()
365 }
366}
367
368#[cfg(test)]
369mod tests {
370 use super::*;
371
372 #[test]
373 fn pattern_manager_before() {
374 let mut manager = PatternManager::new();
375
376 let pattern = PersistentPattern::with_response(Pattern::literal("password:"), "secret\n");
377 let id = manager.add_before(pattern);
378
379 let result = manager.check_before("Enter password: ");
380 assert!(result.is_some());
381
382 let (matched_id, action, _pattern) = result.unwrap();
383 assert_eq!(matched_id, id);
384 assert!(matches!(action, HandlerAction::Respond(_)));
385 }
386
387 #[test]
388 fn pattern_manager_priority() {
389 let mut manager = PatternManager::new();
390
391 let low = PersistentPattern::new(
392 Pattern::literal("test"),
393 Box::new(|_| HandlerAction::Respond("low".into())),
394 )
395 .with_priority(10);
396
397 let high = PersistentPattern::new(
398 Pattern::literal("test"),
399 Box::new(|_| HandlerAction::Respond("high".into())),
400 )
401 .with_priority(1);
402
403 manager.add_before(low);
404 manager.add_before(high);
405
406 let result = manager.check_before("test");
407 assert!(result.is_some());
408
409 if let Some((_, HandlerAction::Respond(s), _pattern)) = result {
410 assert_eq!(s, "high");
411 } else {
412 panic!("Expected Respond action");
413 }
414 }
415
416 #[test]
417 fn pattern_manager_disable() {
418 let mut manager = PatternManager::new();
419
420 let pattern = PersistentPattern::with_response(Pattern::literal("test"), "response");
421 let id = manager.add_before(pattern);
422
423 assert!(manager.check_before("test").is_some());
425
426 manager.get_before_mut(&id).unwrap().disable();
428 assert!(manager.check_before("test").is_none());
429 }
430
431 #[test]
432 fn pattern_manager_after() {
433 let mut manager = PatternManager::new();
434
435 let pattern = PersistentPattern::with_response(Pattern::literal("more? "), "yes\n");
436 let id = manager.add_after(pattern);
437
438 let result = manager.check_after("Show more? ");
439 assert!(result.is_some(), "after pattern should match");
440
441 let (matched_id, action, _pattern) = result.unwrap();
442 assert_eq!(matched_id, id);
443 assert!(matches!(action, HandlerAction::Respond(_)));
444
445 assert!(manager.check_before("Show more? ").is_none());
447 assert_eq!(manager.after_count(), 1);
448 assert_eq!(manager.before_count(), 0);
449 }
450
451 #[test]
452 fn pattern_builder() {
453 let manager = PatternBuilder::new()
454 .with_password_handler("secret")
455 .with_yes_handler()
456 .build();
457
458 assert_eq!(manager.before_count(), 2);
459 }
460}