Skip to main content

rs_zero/core/
dependency_config.rs

1use std::collections::BTreeMap;
2
3use crate::core::ConfigFeatureWarning;
4#[cfg(any(feature = "rpc", feature = "discovery-etcd"))]
5use crate::core::CoreError;
6
7mod database;
8mod etcd;
9mod rpc;
10
11pub use database::{DatabaseKindSection, DatabaseSection};
12pub use etcd::{EtcdAuthSection, EtcdBackoffSection, EtcdDiscoverySection};
13pub use rpc::{
14    RpcClientProvider, RpcClientSection, RpcDeadlineSection, RpcLoadBalancePolicySection,
15    RpcLoadBalanceSection, RpcRetrySection, RpcStreamingSection,
16};
17
18/// Framework-owned RPC client dependencies keyed by logical client name.
19pub type RpcClientsConfig = BTreeMap<String, RpcClientSection>;
20
21/// Returns feature warnings for dependency sections shared by REST/RPC configs.
22pub fn dependency_feature_warnings(
23    rpc_clients: &RpcClientsConfig,
24    database: Option<&DatabaseSection>,
25) -> Vec<ConfigFeatureWarning> {
26    let mut warnings = Vec::new();
27    if !rpc_clients.is_empty() && !cfg!(feature = "rpc") {
28        warnings.push(ConfigFeatureWarning::ignored("rpc_clients", "rpc"));
29    }
30    if rpc_clients
31        .values()
32        .any(|client| client.provider == RpcClientProvider::Etcd)
33        && !cfg!(feature = "discovery-etcd")
34    {
35        warnings.push(ConfigFeatureWarning::ignored(
36            "rpc_clients.*.etcd",
37            "discovery-etcd",
38        ));
39    }
40    if let Some(database) = database {
41        let required_feature = match database.kind {
42            DatabaseKindSection::Sqlite if !cfg!(feature = "db-sqlite") => Some("db-sqlite"),
43            DatabaseKindSection::Postgres if !cfg!(feature = "db-postgres") => Some("db-postgres"),
44            DatabaseKindSection::Mysql if !cfg!(feature = "db-mysql") => Some("db-mysql"),
45            _ => None,
46        };
47        if let Some(required_feature) = required_feature {
48            warnings.push(ConfigFeatureWarning::ignored(
49                "database.kind",
50                required_feature,
51            ));
52        }
53    }
54    warnings
55}
56
57#[cfg(any(feature = "rpc", feature = "discovery-etcd"))]
58fn config_error(message: impl Into<String>) -> CoreError {
59    config::ConfigError::Message(message.into()).into()
60}
61
62#[cfg(test)]
63mod tests {
64    use super::{DatabaseKindSection, DatabaseSection, RpcClientProvider, RpcClientSection};
65
66    #[test]
67    fn default_rpc_client_is_static_without_endpoint() {
68        let client = RpcClientSection::default();
69        assert_eq!(client.provider, RpcClientProvider::Static);
70        assert!(client.endpoint.is_empty());
71        assert!(client.retry.enabled);
72    }
73
74    #[test]
75    fn database_defaults_to_sqlite_memory() {
76        let database = DatabaseSection::default();
77        assert_eq!(database.kind, DatabaseKindSection::Sqlite);
78        assert_eq!(database.url, "sqlite::memory:");
79    }
80
81    #[cfg(feature = "rpc")]
82    #[test]
83    fn maps_rpc_client_runtime_config() {
84        let client = RpcClientSection {
85            endpoint: "http://127.0.0.1:50051".to_string(),
86            service: "hello-rpc".to_string(),
87            ..RpcClientSection::default()
88        };
89        let config = client.to_rpc_client_config().expect("rpc client config");
90        assert_eq!(config.endpoint, "http://127.0.0.1:50051");
91        assert_eq!(config.discovery.service.as_deref(), Some("hello-rpc"));
92        assert!(config.retry.enabled);
93    }
94}