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 e48536dfbe - Show all commits

View file

@ -1,6 +1,71 @@
use super::SystemEvent; use super::{EventSource, SystemEvent, now_unix};
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::process::Command;
use tokio::sync::mpsc; use tokio::sync::mpsc;
pub async fn watch(_tx: mpsc::Sender<SystemEvent>) { pub async fn watch(tx: mpsc::Sender<SystemEvent>) {
// implemented in Task 6 let mut child = match Command::new("journalctl")
.args(["--follow", "--output=json", "--lines=0"])
.stdout(std::process::Stdio::piped())
.spawn()
{
Ok(c) => c,
Err(e) => {
log::error!("journald watcher: failed to spawn journalctl: {e}");
return;
}
};
let stdout = match child.stdout.take() {
Some(s) => s,
None => return,
};
let mut lines = BufReader::new(stdout).lines();
while let Ok(Some(line)) = lines.next_line().await {
if let Some(msg) = extract_message(&line) {
let _ = tx
.send(SystemEvent {
source: EventSource::Journald,
raw_line: msg,
timestamp: now_unix(),
})
.await;
}
}
}
fn extract_message(line: &str) -> Option<String> {
let json: serde_json::Value = serde_json::from_str(line).ok()?;
let msg = json.get("MESSAGE")?.as_str()?;
if msg.is_empty() {
return None;
}
Some(msg.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extract_message_from_journald_json() {
let line = r#"{"MESSAGE":"AUR build failed for foo","PRIORITY":"3","_COMM":"makepkg"}"#;
assert_eq!(
extract_message(line),
Some("AUR build failed for foo".to_string())
);
}
#[test]
fn extract_message_missing_returns_none() {
let line = r#"{"PRIORITY":"6","_COMM":"systemd"}"#;
assert_eq!(extract_message(line), None);
}
#[test]
fn extract_message_empty_skipped() {
let line = r#"{"MESSAGE":"","PRIORITY":"6"}"#;
assert_eq!(extract_message(line), None);
}
} }