Skip to main content

orcs_runtime/components/
noop.rs

1//! NoopComponent - Minimal component for channel binding.
2//!
3//! `NoopComponent` is a minimal Component implementation that:
4//! - Accepts all requests and returns success
5//! - Handles all signals appropriately
6//!
7//! Use this when a Channel needs a bound Component but no specific
8//! functionality is required (e.g., IO channel that routes events elsewhere).
9
10use orcs_component::{Component, ComponentError, Status};
11use orcs_event::{EventCategory, Request, Signal, SignalResponse};
12use orcs_types::ComponentId;
13use serde_json::Value;
14
15/// Minimal Component that does nothing but satisfy the 1:1 Channel binding.
16///
17/// # Example
18///
19/// ```ignore
20/// use orcs_runtime::components::NoopComponent;
21///
22/// let component = NoopComponent::new("io");
23/// engine.spawn_runner(channel_id, Box::new(component));
24/// ```
25pub struct NoopComponent {
26    id: ComponentId,
27    status: Status,
28}
29
30impl NoopComponent {
31    /// Creates a new NoopComponent with the given name.
32    #[must_use]
33    pub fn new(name: &str) -> Self {
34        Self {
35            id: ComponentId::builtin(name),
36            status: Status::Idle,
37        }
38    }
39}
40
41impl Component for NoopComponent {
42    fn id(&self) -> &ComponentId {
43        &self.id
44    }
45
46    fn subscriptions(&self) -> &[EventCategory] {
47        &[EventCategory::Lifecycle]
48    }
49
50    fn status(&self) -> Status {
51        self.status
52    }
53
54    fn on_request(&mut self, request: &Request) -> Result<Value, ComponentError> {
55        // Return the payload as-is (echo behavior)
56        Ok(request.payload.clone())
57    }
58
59    fn on_signal(&mut self, signal: &Signal) -> SignalResponse {
60        if signal.is_veto() {
61            self.status = Status::Aborted;
62            SignalResponse::Abort
63        } else {
64            SignalResponse::Handled
65        }
66    }
67
68    fn abort(&mut self) {
69        self.status = Status::Aborted;
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use orcs_types::{ChannelId, Principal, PrincipalId};
77
78    #[test]
79    fn noop_creation() {
80        let comp = NoopComponent::new("test");
81        assert_eq!(comp.id().name, "test");
82        assert_eq!(comp.status(), Status::Idle);
83    }
84
85    #[test]
86    fn noop_handles_request() {
87        let mut comp = NoopComponent::new("test");
88        let source = ComponentId::builtin("source");
89        let channel = ChannelId::new();
90        let req = Request::new(
91            EventCategory::Echo,
92            "echo",
93            source,
94            channel,
95            Value::String("hello".into()),
96        );
97
98        let result = comp.on_request(&req);
99        assert!(result.is_ok());
100        assert_eq!(result.unwrap(), Value::String("hello".into()));
101    }
102
103    #[test]
104    fn noop_handles_veto() {
105        let mut comp = NoopComponent::new("test");
106        let signal = Signal::veto(Principal::User(PrincipalId::new()));
107
108        let resp = comp.on_signal(&signal);
109        assert_eq!(resp, SignalResponse::Abort);
110        assert_eq!(comp.status(), Status::Aborted);
111    }
112}