rs_zero/rpc/
service_config.rs1use std::{collections::BTreeMap, net::SocketAddr};
2
3use serde::Deserialize;
4
5use crate::core::{
6 ConfigFeatureWarning, CoreError, CoreResult, DatabaseSection, LogConfig, LogSection,
7 RpcClientSection, ServiceConfig, dependency_feature_warnings, load_config,
8};
9
10#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
12#[serde(default, deny_unknown_fields)]
13pub struct RpcServiceConfig {
14 pub name: String,
16 pub mode: String,
18 pub server: RpcServerSection,
20 pub log: LogSection,
22 pub middlewares: RpcMiddlewaresSection,
24 pub rpc_clients: BTreeMap<String, RpcClientSection>,
26 pub database: Option<DatabaseSection>,
28}
29
30impl Default for RpcServiceConfig {
31 fn default() -> Self {
32 let service = ServiceConfig::default();
33 Self {
34 name: service.name,
35 mode: service.mode,
36 server: RpcServerSection::default(),
37 log: service.log,
38 middlewares: RpcMiddlewaresSection::default(),
39 rpc_clients: BTreeMap::new(),
40 database: None,
41 }
42 }
43}
44
45impl RpcServiceConfig {
46 pub fn load(basename: &str, env_prefix: &str) -> Result<Self, config::ConfigError> {
48 load_config(basename, env_prefix)
49 }
50
51 pub fn addr(&self) -> CoreResult<SocketAddr> {
53 format!("{}:{}", self.server.host, self.server.port)
54 .parse()
55 .map_err(|error| {
56 config::ConfigError::Message(format!("invalid RPC listen address: {error}")).into()
57 })
58 }
59
60 pub fn log_config(&self) -> LogConfig {
62 self.log.to_log_config(&self.name)
63 }
64
65 pub fn validate_features(&self) -> Vec<ConfigFeatureWarning> {
67 let mut warnings = Vec::new();
68 if self.middlewares.resilience && !cfg!(feature = "resil") {
69 warnings.push(ConfigFeatureWarning::ignored(
70 "middlewares.resilience",
71 "resil",
72 ));
73 }
74 if self.middlewares.streaming && !cfg!(feature = "resil") {
75 warnings.push(ConfigFeatureWarning::ignored(
76 "middlewares.streaming",
77 "resil",
78 ));
79 }
80 warnings.extend(dependency_feature_warnings(
81 &self.rpc_clients,
82 self.database.as_ref(),
83 ));
84 warnings
85 }
86
87 pub fn rpc_server_config(&self) -> CoreResult<crate::rpc::RpcServerConfig> {
89 let mut config = if self.middlewares.resilience {
90 crate::rpc::RpcServerConfig::production_defaults(self.name.clone(), self.addr()?)
91 } else {
92 crate::rpc::RpcServerConfig::new(self.name.clone(), self.addr()?)
93 };
94 if !self.middlewares.streaming {
95 config.streaming = crate::rpc::RpcStreamingConfig::default();
96 }
97 Ok(config)
98 }
99
100 pub fn rpc_client(&self, name: &str) -> CoreResult<&RpcClientSection> {
102 self.rpc_clients.get(name).ok_or_else(|| {
103 CoreError::Config(config::ConfigError::Message(format!(
104 "missing rpc client config: {name}"
105 )))
106 })
107 }
108
109 #[cfg(feature = "rpc")]
111 pub fn rpc_client_config(&self, name: &str) -> CoreResult<crate::rpc::RpcClientConfig> {
112 self.rpc_client(name)?.to_rpc_client_config()
113 }
114
115 #[cfg(feature = "db")]
117 pub fn database_config(&self) -> Option<crate::db::DatabaseConfig> {
118 self.database
119 .as_ref()
120 .map(DatabaseSection::to_database_config)
121 }
122}
123
124#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
126#[serde(default, deny_unknown_fields)]
127pub struct RpcServerSection {
128 pub host: String,
129 pub port: u16,
130 pub timeout_ms: u64,
131 pub health: bool,
132}
133
134impl Default for RpcServerSection {
135 fn default() -> Self {
136 Self {
137 host: "127.0.0.1".to_string(),
138 port: 50051,
139 timeout_ms: 5000,
140 health: true,
141 }
142 }
143}
144
145#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
147#[serde(default, deny_unknown_fields)]
148pub struct RpcMiddlewaresSection {
149 pub resilience: bool,
150 pub streaming: bool,
151}
152
153impl Default for RpcMiddlewaresSection {
154 fn default() -> Self {
155 Self {
156 resilience: true,
157 streaming: true,
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::RpcServiceConfig;
165
166 #[test]
167 fn validate_features_reflects_compile_time_features() {
168 let warnings = RpcServiceConfig::default().validate_features();
169 assert_eq!(
170 warnings
171 .iter()
172 .any(|warning| warning.option == "middlewares.resilience"),
173 !cfg!(feature = "resil")
174 );
175 assert_eq!(
176 warnings
177 .iter()
178 .any(|warning| warning.option == "middlewares.streaming"),
179 !cfg!(feature = "resil")
180 );
181 }
182}