From ee0bd5531946adb8890e3c8b408e12c9925b1a86 Mon Sep 17 00:00:00 2001 From: Rabindra Dhakal Date: Fri, 13 Feb 2026 21:04:29 +0545 Subject: [PATCH 1/4] feat(crates): add soar-events for frontend-agnostic event reporting --- Cargo.lock | 4 + Cargo.toml | 2 + crates/soar-events/Cargo.toml | 11 + crates/soar-events/src/event.rs | 275 +++++++++++++++++ crates/soar-events/src/lib.rs | 503 ++++++++++++++++++++++++++++++++ crates/soar-events/src/sink.rs | 71 +++++ 6 files changed, 866 insertions(+) create mode 100644 crates/soar-events/Cargo.toml create mode 100644 crates/soar-events/src/event.rs create mode 100644 crates/soar-events/src/lib.rs create mode 100644 crates/soar-events/src/sink.rs diff --git a/Cargo.lock b/Cargo.lock index 273895e2..5bd035c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2319,6 +2319,10 @@ dependencies = [ "xattr", ] +[[package]] +name = "soar-events" +version = "0.1.0" + [[package]] name = "soar-package" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 5f9f4d6f..70339d78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "crates/soar-core", "crates/soar-db", "crates/soar-dl", + "crates/soar-events", "crates/soar-package", "crates/soar-registry", "crates/soar-utils", @@ -52,6 +53,7 @@ soar-config = { version = "0.5.0", path = "crates/soar-config" } soar-core = { version = "0.13.0", path = "crates/soar-core" } soar-db = { version = "0.4.0", path = "crates/soar-db" } soar-dl = { version = "0.8.0", path = "crates/soar-dl" } +soar-events = { version = "0.1.0", path = "crates/soar-events" } soar-package = { version = "0.2.3", path = "crates/soar-package" } soar-registry = { version = "0.3.0", path = "crates/soar-registry" } soar-utils = { version = "0.3.0", path = "crates/soar-utils" } diff --git a/crates/soar-events/Cargo.toml b/crates/soar-events/Cargo.toml new file mode 100644 index 00000000..0c2bf369 --- /dev/null +++ b/crates/soar-events/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "soar-events" +version = "0.1.0" +description = "Event system for soar package manager" +authors.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true +keywords.workspace = true +readme.workspace = true +categories.workspace = true diff --git a/crates/soar-events/src/event.rs b/crates/soar-events/src/event.rs new file mode 100644 index 00000000..9d25d6fc --- /dev/null +++ b/crates/soar-events/src/event.rs @@ -0,0 +1,275 @@ +use crate::OperationId; + +/// All event types emitted by soar operations. +#[derive(Debug, Clone)] +pub enum SoarEvent { + /// Download is starting. + DownloadStarting { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + total: u64, + }, + /// Download is resuming from a previous checkpoint. + DownloadResuming { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + current: u64, + total: u64, + }, + /// Download progress update. + DownloadProgress { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + current: u64, + total: u64, + }, + /// Download completed successfully. + DownloadComplete { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + total: u64, + }, + /// Download error, retrying. + DownloadRetry { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + }, + /// Download permanently failed after retries. + DownloadAborted { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + }, + /// Download recovered from an error. + DownloadRecovered { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + }, + /// Verification stage. + Verifying { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + stage: VerifyStage, + }, + /// Install/extraction stage. + Installing { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + stage: InstallStage, + }, + /// Package removal stage. + Removing { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + stage: RemoveStage, + }, + /// Update check for a package. + UpdateCheck { + pkg_name: String, + pkg_id: String, + status: UpdateCheckStatus, + }, + /// Old version cleanup after update. + UpdateCleanup { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + old_version: String, + stage: UpdateCleanupStage, + }, + /// Hook execution event. + Hook { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + hook_name: String, + stage: HookStage, + }, + /// Package execution (run command). + Running { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + stage: RunStage, + }, + /// Build stage (for source packages). + Building { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + stage: BuildStage, + }, + /// Operation completed successfully. + OperationComplete { + op_id: OperationId, + pkg_name: String, + pkg_id: String, + }, + /// Operation failed. + OperationFailed { + op_id: OperationId, + pkg_name: String, + error: String, + }, + /// Repository sync progress. + SyncProgress { repo_name: String, stage: SyncStage }, + /// Batch operation overall progress. + BatchProgress { + completed: u32, + total: u32, + failed: u32, + }, + /// Log message. + Log { level: LogLevel, message: String }, +} + +/// Verification stages. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum VerifyStage { + /// Calculating and verifying checksum (blake3). + Checksum, + /// Verifying signature with repository public key. + Signature, + /// All verification passed. + Passed, + /// Verification failed. + Failed(String), +} + +/// Installation stages. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum InstallStage { + /// Extracting package archive. + Extracting, + /// Extracting a nested archive within the package. + ExtractingNested, + /// Creating binary symlinks in bin directory. + LinkingBinaries, + /// Integrating desktop files, icons, and appstream metadata. + DesktopIntegration, + /// Setting up portable directories. + SetupPortable, + /// Recording installation metadata to database. + RecordingDatabase, + /// Running a hook (post_download, post_extract, post_install). + RunningHook(String), + /// Installation complete. + Complete, +} + +/// Package removal stages. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RemoveStage { + /// Running pre-remove hook. + RunningHook(String), + /// Removing binary symlinks from bin directory. + UnlinkingBinaries, + /// Removing desktop file symlinks. + UnlinkingDesktop, + /// Removing icon symlinks. + UnlinkingIcons, + /// Deleting the package directory. + RemovingDirectory, + /// Cleaning up database records. + CleaningDatabase, + /// Removal complete. + Complete { size_freed: Option }, +} + +/// Repository sync stages. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SyncStage { + /// Fetching metadata from remote. + Fetching, + /// Repository metadata is already up to date (304 Not Modified). + UpToDate, + /// Decompressing metadata (zstd). + Decompressing, + /// Writing metadata to local database. + WritingDatabase, + /// Validating metadata signature. + Validating, + /// Sync complete. + Complete { package_count: Option }, +} + +/// Update check result for a single package. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum UpdateCheckStatus { + /// A newer version is available. + Available { + current_version: String, + new_version: String, + }, + /// Already up to date. + UpToDate { version: String }, + /// Skipped (pinned, no update source, etc.). + Skipped { reason: String }, +} + +/// Old version cleanup stages after update. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum UpdateCleanupStage { + /// Removing the old version. + Removing, + /// Old version cleanup complete. + Complete { size_freed: Option }, + /// Old version kept. + Kept, +} + +/// Hook execution stages. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum HookStage { + /// Hook is starting. + Starting, + /// Hook completed successfully. + Complete, + /// Hook failed. + Failed { exit_code: Option }, +} + +/// Package execution stages (run command). +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RunStage { + /// Using a cached binary (already downloaded). + CacheHit, + /// Binary not cached, downloading. + Downloading, + /// Running the binary. + Executing, + /// Execution finished. + Complete { exit_code: i32 }, +} + +/// Build stages (for source packages). +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BuildStage { + /// Running build command N of M. + Running { + command_index: usize, + total_commands: usize, + }, + /// Build command completed. + CommandComplete { command_index: usize }, + /// Activating sandbox for build. + Sandboxing, +} + +/// Log levels. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LogLevel { + Debug, + Info, + Warning, + Error, +} diff --git a/crates/soar-events/src/lib.rs b/crates/soar-events/src/lib.rs new file mode 100644 index 00000000..e06fd6d5 --- /dev/null +++ b/crates/soar-events/src/lib.rs @@ -0,0 +1,503 @@ +mod event; +mod sink; + +use std::sync::Arc; + +pub use event::*; +pub use sink::*; + +/// Unique identifier for a running operation. +pub type OperationId = u64; + +/// Shared handle to an event sink. +pub type EventSinkHandle = Arc; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_null_sink() { + let sink = NullSink; + sink.emit(SoarEvent::Log { + level: LogLevel::Info, + message: "test".to_string(), + }); + } + + #[test] + fn test_channel_sink() { + let (sink, rx) = ChannelSink::new(); + sink.emit(SoarEvent::DownloadStarting { + op_id: 1, + pkg_name: "test-pkg".to_string(), + pkg_id: "test-pkg-id".to_string(), + total: 1024, + }); + sink.emit(SoarEvent::DownloadProgress { + op_id: 1, + pkg_name: "test-pkg".to_string(), + pkg_id: "test-pkg-id".to_string(), + current: 512, + total: 1024, + }); + sink.emit(SoarEvent::DownloadComplete { + op_id: 1, + pkg_name: "test-pkg".to_string(), + pkg_id: "test-pkg-id".to_string(), + total: 1024, + }); + + let events: Vec<_> = rx.try_iter().collect(); + assert_eq!(events.len(), 3); + + assert!(matches!( + &events[0], + SoarEvent::DownloadStarting { + total: 1024, + .. + } + )); + assert!(matches!( + &events[1], + SoarEvent::DownloadProgress { + current: 512, + .. + } + )); + assert!(matches!(&events[2], SoarEvent::DownloadComplete { .. })); + } + + #[test] + fn test_channel_sink_receiver_dropped() { + let (sink, rx) = ChannelSink::new(); + drop(rx); + sink.emit(SoarEvent::Log { + level: LogLevel::Info, + message: "orphaned".to_string(), + }); + } + + #[test] + fn test_collector_sink() { + let sink = CollectorSink::default(); + assert!(sink.is_empty()); + + sink.emit(SoarEvent::SyncProgress { + repo_name: "bincache".to_string(), + stage: SyncStage::Fetching, + }); + sink.emit(SoarEvent::SyncProgress { + repo_name: "bincache".to_string(), + stage: SyncStage::Complete { + package_count: Some(100), + }, + }); + + assert_eq!(sink.len(), 2); + let events = sink.events(); + assert!(matches!( + &events[0], + SoarEvent::SyncProgress { + stage: SyncStage::Fetching, + .. + } + )); + assert!(matches!( + &events[1], + SoarEvent::SyncProgress { + stage: SyncStage::Complete { .. }, + .. + } + )); + } + + #[test] + fn test_event_sink_handle() { + let sink: EventSinkHandle = Arc::new(NullSink); + sink.emit(SoarEvent::BatchProgress { + completed: 5, + total: 10, + failed: 0, + }); + + let collector = Arc::new(CollectorSink::default()); + let sink: EventSinkHandle = collector.clone(); + sink.emit(SoarEvent::OperationComplete { + op_id: 42, + pkg_name: "pkg".to_string(), + pkg_id: "pkg-id".to_string(), + }); + assert_eq!(collector.len(), 1); + } + + #[test] + fn test_event_sink_is_send_sync() { + fn assert_send_sync() {} + assert_send_sync::(); + assert_send_sync::(); + assert_send_sync::(); + } + + #[test] + fn test_all_event_variants() { + let collector = CollectorSink::default(); + + // Download lifecycle + collector.emit(SoarEvent::DownloadStarting { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + total: 100, + }); + collector.emit(SoarEvent::DownloadResuming { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + current: 50, + total: 100, + }); + collector.emit(SoarEvent::DownloadProgress { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + current: 75, + total: 100, + }); + collector.emit(SoarEvent::DownloadComplete { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + total: 100, + }); + collector.emit(SoarEvent::DownloadRetry { + op_id: 2, + pkg_name: "b".into(), + pkg_id: "b-id".into(), + }); + collector.emit(SoarEvent::DownloadAborted { + op_id: 2, + pkg_name: "b".into(), + pkg_id: "b-id".into(), + }); + collector.emit(SoarEvent::DownloadRecovered { + op_id: 3, + pkg_name: "c".into(), + pkg_id: "c-id".into(), + }); + + // Verification + collector.emit(SoarEvent::Verifying { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + stage: VerifyStage::Checksum, + }); + collector.emit(SoarEvent::Verifying { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + stage: VerifyStage::Signature, + }); + collector.emit(SoarEvent::Verifying { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + stage: VerifyStage::Passed, + }); + collector.emit(SoarEvent::Verifying { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + stage: VerifyStage::Failed("bad checksum".into()), + }); + + // Installation stages + collector.emit(SoarEvent::Installing { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + stage: InstallStage::Extracting, + }); + collector.emit(SoarEvent::Installing { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + stage: InstallStage::ExtractingNested, + }); + collector.emit(SoarEvent::Installing { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + stage: InstallStage::LinkingBinaries, + }); + collector.emit(SoarEvent::Installing { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + stage: InstallStage::DesktopIntegration, + }); + collector.emit(SoarEvent::Installing { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + stage: InstallStage::SetupPortable, + }); + collector.emit(SoarEvent::Installing { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + stage: InstallStage::RecordingDatabase, + }); + collector.emit(SoarEvent::Installing { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + stage: InstallStage::RunningHook("post_install".into()), + }); + collector.emit(SoarEvent::Installing { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + stage: InstallStage::Complete, + }); + + // Removal stages + collector.emit(SoarEvent::Removing { + op_id: 5, + pkg_name: "e".into(), + pkg_id: "e-id".into(), + stage: RemoveStage::RunningHook("pre_remove".into()), + }); + collector.emit(SoarEvent::Removing { + op_id: 5, + pkg_name: "e".into(), + pkg_id: "e-id".into(), + stage: RemoveStage::UnlinkingBinaries, + }); + collector.emit(SoarEvent::Removing { + op_id: 5, + pkg_name: "e".into(), + pkg_id: "e-id".into(), + stage: RemoveStage::UnlinkingDesktop, + }); + collector.emit(SoarEvent::Removing { + op_id: 5, + pkg_name: "e".into(), + pkg_id: "e-id".into(), + stage: RemoveStage::UnlinkingIcons, + }); + collector.emit(SoarEvent::Removing { + op_id: 5, + pkg_name: "e".into(), + pkg_id: "e-id".into(), + stage: RemoveStage::RemovingDirectory, + }); + collector.emit(SoarEvent::Removing { + op_id: 5, + pkg_name: "e".into(), + pkg_id: "e-id".into(), + stage: RemoveStage::CleaningDatabase, + }); + collector.emit(SoarEvent::Removing { + op_id: 5, + pkg_name: "e".into(), + pkg_id: "e-id".into(), + stage: RemoveStage::Complete { + size_freed: Some(1024 * 1024), + }, + }); + + // Update checks + collector.emit(SoarEvent::UpdateCheck { + pkg_name: "f".into(), + pkg_id: "f-id".into(), + status: UpdateCheckStatus::Available { + current_version: "1.0.0".into(), + new_version: "2.0.0".into(), + }, + }); + collector.emit(SoarEvent::UpdateCheck { + pkg_name: "g".into(), + pkg_id: "g-id".into(), + status: UpdateCheckStatus::UpToDate { + version: "1.0.0".into(), + }, + }); + collector.emit(SoarEvent::UpdateCheck { + pkg_name: "h".into(), + pkg_id: "h-id".into(), + status: UpdateCheckStatus::Skipped { + reason: "pinned".into(), + }, + }); + + // Update cleanup + collector.emit(SoarEvent::UpdateCleanup { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + old_version: "1.0.0".into(), + stage: UpdateCleanupStage::Removing, + }); + collector.emit(SoarEvent::UpdateCleanup { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + old_version: "1.0.0".into(), + stage: UpdateCleanupStage::Complete { + size_freed: Some(512), + }, + }); + collector.emit(SoarEvent::UpdateCleanup { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + old_version: "1.0.0".into(), + stage: UpdateCleanupStage::Kept, + }); + + // Hook execution + collector.emit(SoarEvent::Hook { + op_id: 5, + pkg_name: "e".into(), + pkg_id: "e-id".into(), + hook_name: "pre_remove".into(), + stage: HookStage::Starting, + }); + collector.emit(SoarEvent::Hook { + op_id: 5, + pkg_name: "e".into(), + pkg_id: "e-id".into(), + hook_name: "pre_remove".into(), + stage: HookStage::Complete, + }); + collector.emit(SoarEvent::Hook { + op_id: 6, + pkg_name: "i".into(), + pkg_id: "i-id".into(), + hook_name: "post_install".into(), + stage: HookStage::Failed { + exit_code: Some(1), + }, + }); + + // Run command + collector.emit(SoarEvent::Running { + op_id: 7, + pkg_name: "j".into(), + pkg_id: "j-id".into(), + stage: RunStage::CacheHit, + }); + collector.emit(SoarEvent::Running { + op_id: 8, + pkg_name: "k".into(), + pkg_id: "k-id".into(), + stage: RunStage::Downloading, + }); + collector.emit(SoarEvent::Running { + op_id: 7, + pkg_name: "j".into(), + pkg_id: "j-id".into(), + stage: RunStage::Executing, + }); + collector.emit(SoarEvent::Running { + op_id: 7, + pkg_name: "j".into(), + pkg_id: "j-id".into(), + stage: RunStage::Complete { + exit_code: 0, + }, + }); + + // Build + collector.emit(SoarEvent::Building { + op_id: 4, + pkg_name: "d".into(), + pkg_id: "d-id".into(), + stage: BuildStage::Sandboxing, + }); + collector.emit(SoarEvent::Building { + op_id: 4, + pkg_name: "d".into(), + pkg_id: "d-id".into(), + stage: BuildStage::Running { + command_index: 0, + total_commands: 3, + }, + }); + collector.emit(SoarEvent::Building { + op_id: 4, + pkg_name: "d".into(), + pkg_id: "d-id".into(), + stage: BuildStage::CommandComplete { + command_index: 0, + }, + }); + + // Operation completion + collector.emit(SoarEvent::OperationComplete { + op_id: 1, + pkg_name: "a".into(), + pkg_id: "a-id".into(), + }); + collector.emit(SoarEvent::OperationFailed { + op_id: 2, + pkg_name: "b".into(), + error: "not found".into(), + }); + + // Sync stages + collector.emit(SoarEvent::SyncProgress { + repo_name: "repo".into(), + stage: SyncStage::Fetching, + }); + collector.emit(SoarEvent::SyncProgress { + repo_name: "repo".into(), + stage: SyncStage::UpToDate, + }); + collector.emit(SoarEvent::SyncProgress { + repo_name: "repo".into(), + stage: SyncStage::Decompressing, + }); + collector.emit(SoarEvent::SyncProgress { + repo_name: "repo".into(), + stage: SyncStage::WritingDatabase, + }); + collector.emit(SoarEvent::SyncProgress { + repo_name: "repo".into(), + stage: SyncStage::Validating, + }); + collector.emit(SoarEvent::SyncProgress { + repo_name: "repo".into(), + stage: SyncStage::Complete { + package_count: Some(500), + }, + }); + + // Batch + Log + collector.emit(SoarEvent::BatchProgress { + completed: 5, + total: 10, + failed: 1, + }); + collector.emit(SoarEvent::Log { + level: LogLevel::Debug, + message: "debug".into(), + }); + collector.emit(SoarEvent::Log { + level: LogLevel::Info, + message: "info".into(), + }); + collector.emit(SoarEvent::Log { + level: LogLevel::Warning, + message: "warning".into(), + }); + collector.emit(SoarEvent::Log { + level: LogLevel::Error, + message: "error".into(), + }); + + assert_eq!(collector.len(), 55); + } +} diff --git a/crates/soar-events/src/sink.rs b/crates/soar-events/src/sink.rs new file mode 100644 index 00000000..5301e489 --- /dev/null +++ b/crates/soar-events/src/sink.rs @@ -0,0 +1,71 @@ +use std::sync::mpsc::{self, Receiver, Sender}; + +use crate::SoarEvent; + +/// Trait for consuming events. +/// +/// Each frontend provides its own implementation. +pub trait EventSink: Send + Sync { + fn emit(&self, event: SoarEvent); +} + +/// Channel-based event sink. +/// +/// Sends events through a standard mpsc channel. The receiver end +/// can be polled by any consumer (GUI, test harness, etc.). +pub struct ChannelSink { + sender: Sender, +} + +impl ChannelSink { + pub fn new() -> (Self, Receiver) { + let (sender, receiver) = mpsc::channel(); + ( + Self { + sender, + }, + receiver, + ) + } +} + +impl EventSink for ChannelSink { + fn emit(&self, event: SoarEvent) { + let _ = self.sender.send(event); + } +} + +/// No-op event sink for tests or headless operation. +pub struct NullSink; + +impl EventSink for NullSink { + fn emit(&self, _event: SoarEvent) {} +} + +/// Collector sink that stores all events for inspection. +/// +/// Useful in tests to verify that expected events were emitted. +#[derive(Default)] +pub struct CollectorSink { + events: std::sync::Mutex>, +} + +impl CollectorSink { + pub fn events(&self) -> Vec { + self.events.lock().unwrap().clone() + } + + pub fn len(&self) -> usize { + self.events.lock().unwrap().len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl EventSink for CollectorSink { + fn emit(&self, event: SoarEvent) { + self.events.lock().unwrap().push(event); + } +} From 8464d2f4d6f76588678794bd0b34dfab79c18562 Mon Sep 17 00:00:00 2001 From: Rabindra Dhakal Date: Fri, 13 Feb 2026 21:06:11 +0545 Subject: [PATCH 2/4] set initial version to 0.0.0 --- crates/soar-events/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/soar-events/Cargo.toml b/crates/soar-events/Cargo.toml index 0c2bf369..03cdd9aa 100644 --- a/crates/soar-events/Cargo.toml +++ b/crates/soar-events/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "soar-events" -version = "0.1.0" +version = "0.0.0" description = "Event system for soar package manager" authors.workspace = true license.workspace = true From d3491920e0c0d4a18bc0a22407eaad77a798073f Mon Sep 17 00:00:00 2001 From: Rabindra Dhakal Date: Fri, 13 Feb 2026 21:06:38 +0545 Subject: [PATCH 3/4] update lockfile --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 5bd035c2..96b3f3a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2321,7 +2321,7 @@ dependencies = [ [[package]] name = "soar-events" -version = "0.1.0" +version = "0.0.0" [[package]] name = "soar-package" From 8d3093e4a270b43be935ad92f33082805df2e38a Mon Sep 17 00:00:00 2001 From: Rabindra Dhakal Date: Fri, 13 Feb 2026 21:17:49 +0545 Subject: [PATCH 4/4] fix --- Cargo.toml | 2 +- crates/soar-events/src/event.rs | 1 + crates/soar-events/src/lib.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 70339d78..4c9ce7c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ soar-config = { version = "0.5.0", path = "crates/soar-config" } soar-core = { version = "0.13.0", path = "crates/soar-core" } soar-db = { version = "0.4.0", path = "crates/soar-db" } soar-dl = { version = "0.8.0", path = "crates/soar-dl" } -soar-events = { version = "0.1.0", path = "crates/soar-events" } +soar-events = { version = "0.0.0", path = "crates/soar-events" } soar-package = { version = "0.2.3", path = "crates/soar-package" } soar-registry = { version = "0.3.0", path = "crates/soar-registry" } soar-utils = { version = "0.3.0", path = "crates/soar-utils" } diff --git a/crates/soar-events/src/event.rs b/crates/soar-events/src/event.rs index 9d25d6fc..4f068282 100644 --- a/crates/soar-events/src/event.rs +++ b/crates/soar-events/src/event.rs @@ -118,6 +118,7 @@ pub enum SoarEvent { OperationFailed { op_id: OperationId, pkg_name: String, + pkg_id: String, error: String, }, /// Repository sync progress. diff --git a/crates/soar-events/src/lib.rs b/crates/soar-events/src/lib.rs index e06fd6d5..142c5be3 100644 --- a/crates/soar-events/src/lib.rs +++ b/crates/soar-events/src/lib.rs @@ -444,6 +444,7 @@ mod tests { collector.emit(SoarEvent::OperationFailed { op_id: 2, pkg_name: "b".into(), + pkg_id: "b-id".into(), error: "not found".into(), });