opssignal_core/providers/
console.rs1use crate::delivery::Provider;
2use crate::signal::{Signal, SignalError};
3use crate::template::{self, Templates};
4
5#[derive(Default)]
10pub struct ConsoleProvider {
11 templates: Option<Templates>,
12}
13
14impl ConsoleProvider {
15 pub fn new() -> Self {
16 Self::default()
17 }
18
19 fn line_for(&self, signal: &Signal) -> String {
20 template::render(&self.templates, "text", signal, &[]).unwrap_or_else(|| {
21 format!(
22 "[signal:{}] {} {} — {}",
23 signal.source, signal.severity, signal.event_type, signal.title
24 )
25 })
26 }
27
28 pub fn with_templates(mut self, templates: Option<Templates>) -> Self {
30 self.templates = templates;
31 self
32 }
33}
34
35#[async_trait::async_trait]
36impl Provider for ConsoleProvider {
37 fn name(&self) -> &'static str {
38 "console"
39 }
40
41 fn preview(&self, signal: &Signal) -> Option<serde_json::Value> {
42 Some(serde_json::json!({ "text": self.line_for(signal) }))
43 }
44
45 async fn send(&self, signal: &Signal) -> Result<(), SignalError> {
46 println!("{}", self.line_for(signal));
47 Ok(())
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use crate::signal::SignalInput;
55
56 #[tokio::test]
57 async fn console_always_succeeds() {
58 let signal = Signal::from_input(SignalInput {
59 source: "x".into(),
60 event_type: "y".into(),
61 title: "z".into(),
62 ..Default::default()
63 });
64 assert_eq!(ConsoleProvider::new().name(), "console");
65 ConsoleProvider::new()
66 .send(&signal)
67 .await
68 .expect("console never fails");
69 }
70
71 #[tokio::test]
72 async fn text_template_is_accepted() {
73 let provider = ConsoleProvider::new()
74 .with_templates(template::for_test("console", &[("text", "{{ title }}")]));
75 provider
76 .send(&Signal::from_input(SignalInput {
77 source: "t".into(),
78 event_type: "e".into(),
79 title: "hello".into(),
80 ..Default::default()
81 }))
82 .await
83 .expect("console never fails");
84 }
85}