feat: full pattern matrix — M1 complete, M2 LLM chat, 30+ pattern files #10

Open
pyr0ball wants to merge 31 commits from feat/patterns-expansion into main
Showing only changes of commit c0f046bd7c - Show all commits

View file

@ -9,14 +9,16 @@ use crate::patterns::MatchedEvent;
static PENDING: Mutex<Vec<MatchedEvent>> = Mutex::new(vec![]);
pub fn dispatch<R: Runtime>(app: &AppHandle<R>, event: MatchedEvent) {
let level = {
let state = app.state::<AppState>();
let config = state.config.lock().unwrap();
config.display.notification_level.clone()
};
let level = app
.state::<AppState>()
.config
.lock()
.map(|c| c.display.notification_level.clone())
.unwrap_or_default();
if let Ok(mut pending) = PENDING.lock() {
pending.push(event.clone());
match PENDING.lock() {
Ok(mut pending) => pending.push(event.clone()),
Err(e) => log::warn!("notify: pending queue lock poisoned — event dropped: {e}"),
}
match level {
@ -48,3 +50,36 @@ pub fn take_pending() -> Vec<MatchedEvent> {
.map(|mut v| std::mem::take(&mut *v))
.unwrap_or_default()
}
#[cfg(test)]
mod tests {
use super::*;
fn make_event(id: &str) -> MatchedEvent {
MatchedEvent {
pattern_id: id.into(),
title: "Title".into(),
body: "Body".into(),
severity: "warn".into(),
timestamp: 0,
}
}
#[test]
fn take_pending_drains_queue() {
// Ensure clean state — drain any events from other tests sharing the static
let _ = take_pending();
if let Ok(mut q) = PENDING.lock() {
q.push(make_event("a"));
q.push(make_event("b"));
}
let first = take_pending();
assert_eq!(first.len(), 2);
assert_eq!(first[0].pattern_id, "a");
let second = take_pending();
assert!(second.is_empty(), "queue must be empty after drain");
}
}