diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 3fdaa91..ae63786 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -24,7 +24,7 @@ pub fn run() { }) .setup(|app| { tray::build_tray(&app.handle())?; - watcher::spawn(); + watcher::spawn(std::collections::HashMap::new()); Ok(()) }) .invoke_handler(tauri::generate_handler![ diff --git a/src-tauri/src/watcher.rs b/src-tauri/src/watcher.rs deleted file mode 100644 index 9512fde..0000000 --- a/src-tauri/src/watcher.rs +++ /dev/null @@ -1,38 +0,0 @@ -/// System event watcher — M1 implementation. -/// -/// M0 stub: defines the types and the spawn interface so the rest of the app -/// can wire up event handling now. Actual journald/dmesg reading lands in M1. -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum EventSeverity { - Info, - Warn, - Crit, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "type", rename_all = "snake_case")] -pub enum EventSource { - Journald, - Kmsg, - AppLog { app: String }, -} - -#[derive(Debug, Clone, Serialize)] -pub struct SystemEvent { - pub source: EventSource, - pub raw_line: String, - pub timestamp: u64, -} - -/// Starts the background watcher task. -/// M0: no-op placeholder — returns immediately. -/// M1: spawns a tokio task reading journald + dmesg and emitting events. -pub fn spawn() { - // TODO(M1): spawn tokio::task reading journald via sd-journal crate - // TODO(M1): spawn dmesg poller for kernel messages - // TODO(M1): emit SystemEvent via tauri app_handle.emit() - log::info!("watcher: stub — no-op until M1"); -} diff --git a/src-tauri/src/watcher/inotify.rs b/src-tauri/src/watcher/inotify.rs new file mode 100644 index 0000000..ecf30e2 --- /dev/null +++ b/src-tauri/src/watcher/inotify.rs @@ -0,0 +1,7 @@ +use super::SystemEvent; +use std::collections::HashMap; +use tokio::sync::mpsc; + +pub async fn watch(_log_paths: HashMap, _tx: mpsc::Sender) { + // implemented in Task 8 +} diff --git a/src-tauri/src/watcher/journald.rs b/src-tauri/src/watcher/journald.rs new file mode 100644 index 0000000..01f1d00 --- /dev/null +++ b/src-tauri/src/watcher/journald.rs @@ -0,0 +1,6 @@ +use super::SystemEvent; +use tokio::sync::mpsc; + +pub async fn watch(_tx: mpsc::Sender) { + // implemented in Task 6 +} diff --git a/src-tauri/src/watcher/kmsg.rs b/src-tauri/src/watcher/kmsg.rs new file mode 100644 index 0000000..3c2c47d --- /dev/null +++ b/src-tauri/src/watcher/kmsg.rs @@ -0,0 +1,6 @@ +use super::SystemEvent; +use tokio::sync::mpsc; + +pub async fn watch(_tx: mpsc::Sender) { + // implemented in Task 7 +} diff --git a/src-tauri/src/watcher/mod.rs b/src-tauri/src/watcher/mod.rs new file mode 100644 index 0000000..fd48b19 --- /dev/null +++ b/src-tauri/src/watcher/mod.rs @@ -0,0 +1,85 @@ +pub mod inotify; +pub mod journald; +pub mod kmsg; + +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::time::{SystemTime, UNIX_EPOCH}; +use tokio::sync::mpsc; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum EventSeverity { + Info, + Warn, + Crit, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum EventSource { + Journald, + Kmsg, + AppLog { app: String }, +} + +#[derive(Debug, Clone, Serialize)] +pub struct SystemEvent { + pub source: EventSource, + pub raw_line: String, + pub timestamp: u64, +} + +pub fn now_unix() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_secs() +} + +/// Spawns all watcher tasks. Returns the receiver end of the event channel. +/// `log_paths` comes from the loaded PatternFile. +pub fn spawn(log_paths: HashMap) -> mpsc::Receiver { + let (tx, rx) = mpsc::channel::(256); + + let tx_j = tx.clone(); + tauri::async_runtime::spawn(async move { + journald::watch(tx_j).await; + }); + + let tx_k = tx.clone(); + tauri::async_runtime::spawn(async move { + kmsg::watch(tx_k).await; + }); + + tauri::async_runtime::spawn(async move { + inotify::watch(log_paths, tx).await; + }); + + rx +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn event_source_can_be_cloned() { + let s = EventSource::AppLog { + app: "steam".into(), + }; + let _ = s.clone(); + assert!(matches!(s, EventSource::AppLog { .. })); + } + + #[test] + fn system_event_constructed() { + let e = SystemEvent { + source: EventSource::Journald, + raw_line: "test line".into(), + timestamp: now_unix(), + }; + assert_eq!(e.raw_line, "test line"); + assert!(e.timestamp > 0); + } +}