Skip to main content

razeri_plugin_api/sinks/
stdout.rs

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