Skip to main content

camel_component_sql/
bundle.rs

1use std::sync::Arc;
2
3use camel_component_api::{CamelError, ComponentBundle, ComponentRegistrar};
4
5use crate::{SqlComponent, config::SqlGlobalConfig};
6
7pub struct SqlBundle {
8    config: SqlGlobalConfig,
9    catalog: Option<Arc<dyn camel_api::datasource::DatasourceCatalog>>,
10}
11
12impl ComponentBundle for SqlBundle {
13    fn config_key() -> &'static str {
14        "sql"
15    }
16
17    fn from_toml(value: toml::Value) -> Result<Self, CamelError> {
18        let config: SqlGlobalConfig = value
19            .try_into()
20            .map_err(|e: toml::de::Error| CamelError::Config(e.to_string()))?;
21        Ok(Self {
22            config,
23            catalog: None,
24        })
25    }
26
27    fn register_all(self, ctx: &mut dyn ComponentRegistrar) {
28        let component = match self.catalog {
29            Some(catalog) => SqlComponent::with_config_and_catalog(self.config, catalog),
30            None => SqlComponent::with_config(self.config),
31        };
32        ctx.register_component_dyn(Arc::new(component));
33    }
34}
35
36impl SqlBundle {
37    pub fn with_catalog(
38        mut self,
39        catalog: Arc<dyn camel_api::datasource::DatasourceCatalog>,
40    ) -> Self {
41        if let Err(e) =
42            catalog.register_factory("sqlx", Arc::new(crate::pool_factory::SqlPoolFactory))
43        {
44            // log-policy: outside-contract
45            tracing::warn!("failed to register sqlx pool factory: {}", e);
46        }
47        self.catalog = Some(catalog);
48        self
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55    use camel_component_api::ComponentBundle;
56
57    struct TestRegistrar {
58        schemes: Vec<String>,
59    }
60
61    impl camel_component_api::ComponentRegistrar for TestRegistrar {
62        fn register_component_dyn(
63            &mut self,
64            component: std::sync::Arc<dyn camel_component_api::Component>,
65        ) {
66            self.schemes.push(component.scheme().to_string());
67        }
68    }
69
70    #[test]
71    fn sql_bundle_from_toml_empty_uses_defaults() {
72        let value: toml::Value = toml::from_str("").unwrap();
73        let result = SqlBundle::from_toml(value);
74        assert!(
75            result.is_ok(),
76            "empty TOML must use defaults: {:?}",
77            result.err()
78        );
79    }
80
81    #[test]
82    fn sql_bundle_registers_expected_schemes() {
83        let bundle = SqlBundle::from_toml(toml::Value::Table(toml::map::Map::new())).unwrap();
84        let mut registrar = TestRegistrar { schemes: vec![] };
85
86        bundle.register_all(&mut registrar);
87
88        assert_eq!(registrar.schemes, vec!["sql"]);
89    }
90
91    #[test]
92    fn sql_bundle_from_toml_returns_error_on_invalid_config() {
93        let mut table = toml::map::Map::new();
94        table.insert(
95            "max_connections".to_string(),
96            toml::Value::String("not-a-number".to_string()),
97        );
98
99        let result = SqlBundle::from_toml(toml::Value::Table(table));
100        assert!(result.is_err(), "expected Err on malformed config");
101        let err_msg = match result {
102            Err(err) => err.to_string(),
103            Ok(_) => panic!("expected Err on malformed config"),
104        };
105        assert!(
106            err_msg.contains("Configuration error"),
107            "expected CamelError::Config, got: {err_msg}"
108        );
109    }
110}