Skip to main content

camel_component_api/
component.rs

1use async_trait::async_trait;
2use camel_api::CamelError;
3use camel_api::component_metadata::ComponentMetadata;
4
5use crate::component_context::ComponentContext;
6use crate::endpoint::Endpoint;
7
8/// A Component is a factory for Endpoints.
9///
10/// Each component handles a specific URI scheme (e.g. "timer", "log", "direct").
11#[async_trait]
12pub trait Component: Send + Sync {
13    /// The URI scheme this component handles (e.g., "timer", "log").
14    fn scheme(&self) -> &str;
15
16    /// Declare this component's metadata (URI options, capabilities, version).
17    ///
18    /// Called once at registration time to harvest metadata into the catalog.
19    /// Override to provide rich metadata; the default returns a minimal
20    /// entry with just the scheme name.
21    fn metadata(&self) -> ComponentMetadata {
22        ComponentMetadata::minimal(self.scheme())
23    }
24
25    /// Create an endpoint from a URI string.
26    fn create_endpoint(
27        &self,
28        uri: &str,
29        ctx: &dyn ComponentContext,
30    ) -> Result<Box<dyn Endpoint>, CamelError>;
31
32    /// Start the component (e.g. connect to external systems).
33    ///
34    /// Default: no-op, returns `Ok(())`.
35    async fn start(&self) -> Result<(), CamelError> {
36        Ok(())
37    }
38
39    /// Stop the component (e.g. release resources, close connections).
40    ///
41    /// Default: no-op, returns `Ok(())`.
42    async fn stop(&self) -> Result<(), CamelError> {
43        Ok(())
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    struct DummyComponent;
52
53    #[async_trait]
54    impl Component for DummyComponent {
55        fn scheme(&self) -> &str {
56            "dummy"
57        }
58
59        fn create_endpoint(
60            &self,
61            _uri: &str,
62            _ctx: &dyn ComponentContext,
63        ) -> Result<Box<dyn Endpoint>, CamelError> {
64            Err(CamelError::EndpointCreationFailed("not implemented".into()))
65        }
66    }
67
68    #[test]
69    fn test_component_default_metadata() {
70        let c = DummyComponent;
71        let meta = c.metadata();
72        assert_eq!(meta.scheme, "dummy");
73        assert_eq!(meta.schema_version, ComponentMetadata::SCHEMA_VERSION);
74        assert!(meta.uri_options.is_empty());
75    }
76
77    #[tokio::test]
78    async fn test_component_default_lifecycle() {
79        let c = DummyComponent;
80        assert!(c.start().await.is_ok());
81        assert!(c.stop().await.is_ok());
82    }
83}