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