razeri_plugin_api/
sinks.rs1#[cfg(not(target_arch = "wasm32"))]
14pub mod jsonl;
15pub mod stdout;
16pub mod webhook;
17
18use std::sync::Arc;
19
20use crate::error::EventSinkError;
21use crate::event::EventSink;
22
23#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
25pub struct EventSinkConfig {
26 #[serde(rename = "type")]
28 pub sink_type: String,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub path: Option<String>,
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub url: Option<String>,
35}
36
37pub async fn create_sink(config: &EventSinkConfig) -> Result<Arc<dyn EventSink>, EventSinkError> {
43 match config.sink_type.as_str() {
44 "stdout" => Ok(Arc::new(stdout::StdoutSink::new())),
45 #[cfg(not(target_arch = "wasm32"))]
46 "jsonl" => {
47 let path = config
48 .path
49 .as_deref()
50 .ok_or_else(|| EventSinkError::SinkError("jsonl sink requires 'path'".into()))?;
51 let sink = jsonl::JsonlSink::open(path).await?;
52 Ok(Arc::new(sink))
53 }
54 "webhook" => {
55 let url = config
56 .url
57 .as_deref()
58 .ok_or_else(|| EventSinkError::SinkError("webhook sink requires 'url'".into()))?;
59 Ok(Arc::new(webhook::WebhookSink::new(url)))
60 }
61 other => Err(EventSinkError::SinkError(format!("unknown sink type: {other}"))),
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[tokio::test]
70 async fn create_stdout_sink_from_config() {
71 let config = EventSinkConfig { sink_type: "stdout".into(), path: None, url: None };
72 let sink = create_sink(&config).await.unwrap();
73 assert_eq!(sink.name(), "stdout");
74 }
75
76 #[tokio::test]
77 async fn create_jsonl_sink_from_config() {
78 let dir = tempfile::tempdir().unwrap();
79 let path = dir.path().join("test.jsonl");
80 let config = EventSinkConfig {
81 sink_type: "jsonl".into(),
82 path: Some(path.to_string_lossy().into()),
83 url: None,
84 };
85 let sink = create_sink(&config).await.unwrap();
86 assert_eq!(sink.name(), "jsonl");
87 }
88
89 #[tokio::test]
90 async fn create_webhook_sink_from_config() {
91 let config = EventSinkConfig {
92 sink_type: "webhook".into(),
93 path: None,
94 url: Some("https://example.com/events".into()),
95 };
96 let sink = create_sink(&config).await.unwrap();
97 assert_eq!(sink.name(), "webhook");
98 }
99
100 #[tokio::test]
101 async fn create_jsonl_sink_missing_path_returns_error() {
102 let config = EventSinkConfig { sink_type: "jsonl".into(), path: None, url: None };
103 let result = create_sink(&config).await;
104 assert!(result.is_err());
105 }
106
107 #[tokio::test]
108 async fn create_webhook_sink_missing_url_returns_error() {
109 let config = EventSinkConfig { sink_type: "webhook".into(), path: None, url: None };
110 let result = create_sink(&config).await;
111 assert!(result.is_err());
112 }
113
114 #[tokio::test]
115 async fn create_unknown_sink_returns_error() {
116 let config = EventSinkConfig { sink_type: "kafka".into(), path: None, url: None };
117 let result = create_sink(&config).await;
118 assert!(result.is_err());
119 }
120}