Skip to main content

razeri_plugin_api/sinks/
stdout.rs

1//! Stdout event sink — writes events as JSON to stderr.
2//!
3//! Uses stderr rather than stdout so it does not interfere with
4//! structured program output on stdout.
5
6use std::sync::Arc;
7
8use async_trait::async_trait;
9
10use crate::builder::EventSinkRegistration;
11use crate::error::EventSinkError;
12use crate::event::{EventSink, RazeriEvent};
13
14/// Writes each event as a single-line JSON object to stderr.
15pub struct StdoutSink;
16
17impl StdoutSink {
18    /// Create a new `StdoutSink`.
19    #[must_use]
20    pub fn new() -> Self {
21        Self
22    }
23}
24
25impl Default for StdoutSink {
26    fn default() -> Self {
27        Self::new()
28    }
29}
30
31#[async_trait]
32impl EventSink for StdoutSink {
33    fn name(&self) -> &'static str {
34        "stdout"
35    }
36
37    async fn on_event(&self, event: &RazeriEvent) -> Result<(), EventSinkError> {
38        let json = serde_json::to_string(event)
39            .map_err(|e| EventSinkError::SinkError(format!("serialization failed: {e}")))?;
40        eprintln!("{json}");
41        Ok(())
42    }
43
44    async fn flush(&self) -> Result<(), EventSinkError> {
45        Ok(())
46    }
47}
48
49inventory::submit! {
50    EventSinkRegistration::new("stdout", || Arc::new(StdoutSink::new()))
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use crate::event::{RazeriEvent, WorkflowContext};
57    use chrono::Utc;
58
59    fn test_workflow() -> WorkflowContext {
60        WorkflowContext::new("test-wf").unwrap()
61    }
62
63    #[tokio::test]
64    async fn stdout_sink_accepts_events() {
65        let sink = StdoutSink::new();
66        assert_eq!(sink.name(), "stdout");
67
68        let event = RazeriEvent::design_validated(
69            "test-stdout-1",
70            Utc::now(),
71            test_workflow(),
72            "test.yaml",
73            0,
74            0,
75        )
76        .unwrap();
77        let result = sink.on_event(&event).await;
78        assert!(result.is_ok());
79    }
80
81    #[tokio::test]
82    async fn stdout_sink_flush_is_noop() {
83        let sink = StdoutSink::new();
84        assert!(sink.flush().await.is_ok());
85    }
86}