Skip to main content

omni_dev/daemon/services/
echo.rs

1//! A trivial [`DaemonService`] that echoes its payload back. It exists so the
2//! daemon framework can be exercised end-to-end before — and independently of
3//! — any real service.
4
5use anyhow::{bail, Result};
6use async_trait::async_trait;
7use serde_json::Value;
8
9use crate::daemon::service::{DaemonService, MenuItem, MenuSnapshot, ServiceStatus};
10
11/// A stateless service whose only op, `echo`, returns its payload verbatim.
12#[derive(Debug, Clone, Copy, Default)]
13pub struct EchoService;
14
15#[async_trait]
16impl DaemonService for EchoService {
17    fn name(&self) -> &'static str {
18        "echo"
19    }
20
21    async fn handle(&self, op: &str, payload: Value) -> Result<Value> {
22        match op {
23            "echo" => Ok(payload),
24            other => bail!("unknown echo op: {other}"),
25        }
26    }
27
28    fn menu(&self) -> MenuSnapshot {
29        MenuSnapshot {
30            title: "Echo".to_string(),
31            items: vec![MenuItem::Label("ready".to_string())],
32        }
33    }
34
35    async fn menu_action(&self, _action_id: &str) -> Result<()> {
36        Ok(())
37    }
38
39    async fn status(&self) -> ServiceStatus {
40        ServiceStatus {
41            name: self.name().to_string(),
42            healthy: true,
43            summary: "ready".to_string(),
44            detail: Value::Null,
45        }
46    }
47
48    async fn shutdown(&self) {}
49}
50
51#[cfg(test)]
52#[allow(clippy::unwrap_used, clippy::expect_used)]
53mod tests {
54    use super::*;
55    use serde_json::json;
56
57    #[tokio::test]
58    async fn echo_service_round_trips_and_reports() {
59        let svc = EchoService;
60        assert_eq!(svc.name(), "echo");
61        // `echo` returns its payload verbatim; any other op errors.
62        assert_eq!(
63            svc.handle("echo", json!({ "a": 1 })).await.unwrap(),
64            json!({ "a": 1 })
65        );
66        assert!(svc.handle("nope", Value::Null).await.is_err());
67        // Menu / status / actions are inert but must not panic.
68        assert_eq!(svc.menu().title, "Echo");
69        svc.menu_action("anything").await.unwrap();
70        let status = svc.status().await;
71        assert_eq!(status.name, "echo");
72        assert!(status.healthy);
73        svc.shutdown().await;
74    }
75}