Skip to main content

camel_template/
component.rs

1//! `template:` scheme Component for the external template component
2//! (ADR-0047 Stage 2, Phase 4 / Task 4.3).
3//!
4//! [`TemplateComponent`] is the factory registered under the URI scheme
5//! `template`. It is a thin parse-only adapter: it parses the URI, resolves
6//! the operator-supplied resource limits, and constructs a
7//! [`TemplateEndpoint`] — it performs NO filesystem access. The actual
8//! template acquisition and compilation happen inside the endpoint's
9//! lifecycle `start()` (Task 4.4).
10//!
11//! [`TemplateEndpoint`]: crate::endpoint::TemplateEndpoint
12
13use camel_api::CamelError;
14use camel_component_api::{Component, ComponentContext, Endpoint};
15use camel_language_api::MinijinjaLimitsConfig;
16
17use crate::config::ExternalTemplateLimitsConfig;
18use crate::endpoint::TemplateEndpoint;
19use crate::uri;
20
21/// Factory for `template:` scheme Endpoints (ADR-0047 Stage 2).
22///
23/// Constructible from operator-supplied configs via [`TemplateComponent::new`];
24/// `Default` derives `MinijinjaLimitsConfig::default()` per the spec. Used by
25/// the bundle (Task 4.5) to register the `template` scheme into a
26/// `CamelContext`.
27#[derive(Debug, Default)]
28pub struct TemplateComponent {
29    limits: ExternalTemplateLimitsConfig,
30    render_limits: MinijinjaLimitsConfig,
31}
32
33impl TemplateComponent {
34    /// Build a component from operator-supplied resource-limit configs.
35    pub fn new(limits: ExternalTemplateLimitsConfig, render_limits: MinijinjaLimitsConfig) -> Self {
36        Self {
37            limits,
38            render_limits,
39        }
40    }
41}
42
43impl Component for TemplateComponent {
44    fn scheme(&self) -> &str {
45        "template"
46    }
47
48    /// Parse-only endpoint creation. The URI is validated and the
49    /// operator-supplied limits are resolved (a zero value fails closed at
50    /// this seam), but the filesystem is NOT touched — the heavy lifting
51    /// happens in the endpoint's `lifecycle().start()` (Task 4.4).
52    fn create_endpoint(
53        &self,
54        uri_str: &str,
55        ctx: &dyn ComponentContext,
56    ) -> Result<Box<dyn Endpoint>, CamelError> {
57        let config = uri::parse_template_uri(uri_str, self.limits.clone())?;
58        // Fail closed on a zero operator-supplied limit. `resolve()`
59        // returns `TemplateReloadError`, which has a `From` impl that maps
60        // to `CamelError::TemplateReload(_)` (see `crate::error`).
61        let limits = config.limits.resolve()?;
62        let route_id = ctx.route_id().unwrap_or("template-endpoint").to_string();
63        Ok(Box::new(TemplateEndpoint::new(
64            uri_str.to_string(),
65            config.entry_abs_path,
66            limits,
67            self.render_limits.clone(),
68            route_id,
69        )))
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    /// The component must register under the URI scheme `template`.
78    /// Anything else breaks the bundle (Task 4.5) and every `to:`
79    /// `template:file:///...` route.
80    #[test]
81    fn component_scheme_is_template() {
82        let component = TemplateComponent::default();
83        assert_eq!(component.scheme(), "template");
84    }
85}