use serde::Serialize;
use tauri::{AppHandle, Emitter, Runtime};
pub mod event_names {
pub const COLLECTION_CREATED: &str = "velesdb://collection-created";
pub const COLLECTION_DELETED: &str = "velesdb://collection-deleted";
pub const COLLECTION_UPDATED: &str = "velesdb://collection-updated";
pub const OPERATION_PROGRESS: &str = "velesdb://operation-progress";
pub const OPERATION_COMPLETE: &str = "velesdb://operation-complete";
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CollectionEventPayload {
pub collection: String,
pub operation: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub count: Option<usize>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ProgressEventPayload {
pub operation_id: String,
pub progress: u8,
pub total: usize,
pub processed: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompleteEventPayload {
pub operation_id: String,
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_ms: Option<u64>,
}
pub fn emit_collection_created<R: Runtime>(app: &AppHandle<R>, collection: &str) {
let payload = CollectionEventPayload {
collection: collection.to_string(),
operation: "created".to_string(),
count: None,
};
let _ = app.emit(event_names::COLLECTION_CREATED, payload);
}
pub fn emit_collection_deleted<R: Runtime>(app: &AppHandle<R>, collection: &str) {
let payload = CollectionEventPayload {
collection: collection.to_string(),
operation: "deleted".to_string(),
count: None,
};
let _ = app.emit(event_names::COLLECTION_DELETED, payload);
}
pub fn emit_collection_updated<R: Runtime>(
app: &AppHandle<R>,
collection: &str,
operation: &str,
count: usize,
) {
let payload = CollectionEventPayload {
collection: collection.to_string(),
operation: operation.to_string(),
count: Some(count),
};
let _ = app.emit(event_names::COLLECTION_UPDATED, payload);
}
pub fn emit_progress<R: Runtime>(
app: &AppHandle<R>,
operation_id: &str,
processed: usize,
total: usize,
message: Option<&str>,
) {
#[allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss
)]
let progress = if total > 0 {
((processed as f64 / total as f64) * 100.0).min(100.0) as u8
} else {
0
};
let payload = ProgressEventPayload {
operation_id: operation_id.to_string(),
progress,
total,
processed,
message: message.map(String::from),
};
let _ = app.emit(event_names::OPERATION_PROGRESS, payload);
}
pub fn emit_complete<R: Runtime>(
app: &AppHandle<R>,
operation_id: &str,
success: bool,
error: Option<&str>,
duration_ms: Option<u64>,
) {
let payload = CompleteEventPayload {
operation_id: operation_id.to_string(),
success,
error: error.map(String::from),
duration_ms,
};
let _ = app.emit(event_names::OPERATION_COMPLETE, payload);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_collection_event_payload_serialization() {
let payload = CollectionEventPayload {
collection: "test".to_string(),
operation: "created".to_string(),
count: None,
};
let json = match serde_json::to_string(&payload) {
Ok(json) => json,
Err(err) => panic!("payload serialization should succeed: {err}"),
};
assert!(json.contains("\"collection\":\"test\""));
assert!(json.contains("\"operation\":\"created\""));
assert!(!json.contains("count")); }
#[test]
fn test_progress_event_payload_serialization() {
let payload = ProgressEventPayload {
operation_id: "op-123".to_string(),
progress: 50,
total: 100,
processed: 50,
message: Some("Processing...".to_string()),
};
let json = match serde_json::to_string(&payload) {
Ok(json) => json,
Err(err) => panic!("payload serialization should succeed: {err}"),
};
assert!(json.contains("\"operationId\":\"op-123\""));
assert!(json.contains("\"progress\":50"));
}
}