#[cfg(not(target_arch = "wasm32"))]
pub mod jsonl;
pub mod stdout;
use std::sync::Arc;
use crate::error::EventSinkError;
use crate::event::EventSink;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EventSinkConfig {
#[serde(rename = "type")]
pub sink_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
}
pub async fn create_sink(config: &EventSinkConfig) -> Result<Arc<dyn EventSink>, EventSinkError> {
match config.sink_type.as_str() {
"stdout" => Ok(Arc::new(stdout::StdoutSink::new())),
#[cfg(not(target_arch = "wasm32"))]
"jsonl" => {
let path = config
.path
.as_deref()
.ok_or_else(|| EventSinkError::SinkError("jsonl sink requires 'path'".into()))?;
let sink = jsonl::JsonlSink::open(path).await?;
Ok(Arc::new(sink))
}
#[cfg(target_arch = "wasm32")]
"jsonl" => Err(EventSinkError::SinkError(
"a `jsonl` sink writes a file, and a program built for the browser has none".into(),
)),
other => Err(EventSinkError::SinkError(format!(
"there is no event sink of type `{other}`: the types are `stdout` and `jsonl`. \
For any other destination, register a sink of your own"
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn create_stdout_sink_from_config() {
let config = EventSinkConfig { sink_type: "stdout".into(), path: None };
let sink = create_sink(&config).await.unwrap();
assert_eq!(sink.name(), "stdout");
}
#[tokio::test]
async fn create_jsonl_sink_from_config() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("test.jsonl");
let config = EventSinkConfig {
sink_type: "jsonl".into(),
path: Some(path.to_string_lossy().into()),
};
let sink = create_sink(&config).await.unwrap();
assert_eq!(sink.name(), "jsonl");
}
#[tokio::test]
async fn create_jsonl_sink_missing_path_returns_error() {
let config = EventSinkConfig { sink_type: "jsonl".into(), path: None };
let result = create_sink(&config).await;
assert!(result.is_err());
}
#[tokio::test]
async fn a_webhook_sink_is_refused_by_name() {
let config = EventSinkConfig { sink_type: "webhook".into(), path: None };
let Err(refusal) = create_sink(&config).await else {
panic!("a webhook sink was built, and it sends nothing");
};
let words = refusal.to_string();
assert!(
words.contains("`webhook`") && words.contains("`stdout`") && words.contains("`jsonl`"),
"{words}"
);
}
#[tokio::test]
async fn create_unknown_sink_returns_error() {
let config = EventSinkConfig { sink_type: "kafka".into(), path: None };
let result = create_sink(&config).await;
assert!(result.is_err());
}
}