1use rskit_validation::Validate;
2use serde::{Deserialize, Serialize};
3use validator::{ValidationError, ValidationErrors};
4
5#[derive(Debug, Clone, Deserialize, Serialize)]
7pub struct TlsConfig {
8 pub cert_path: String,
10 pub key_path: String,
12}
13
14#[derive(Debug, Clone, Deserialize, Serialize)]
16pub struct GrpcServerConfig {
17 pub host: String,
19
20 pub port: u16,
22
23 pub max_connections: Option<usize>,
25
26 pub keep_alive_secs: Option<u64>,
28
29 pub tls: Option<TlsConfig>,
34}
35
36impl Validate for GrpcServerConfig {
37 fn validate(&self) -> Result<(), ValidationErrors> {
38 let mut errors = ValidationErrors::new();
39 if self.host.trim().is_empty() {
40 errors.add("host", ValidationError::new("length"));
41 }
42 if self.port == 0 {
43 errors.add("port", ValidationError::new("range"));
44 }
45 if errors.is_empty() {
46 Ok(())
47 } else {
48 Err(errors)
49 }
50 }
51}
52
53impl Default for GrpcServerConfig {
54 fn default() -> Self {
55 Self {
56 host: "0.0.0.0".into(),
57 port: 50051,
58 max_connections: None,
59 keep_alive_secs: None,
60 tls: None,
61 }
62 }
63}
64
65impl GrpcServerConfig {
66 pub fn new(host: impl Into<String>, port: u16) -> Self {
68 Self {
69 host: host.into(),
70 port,
71 ..Default::default()
72 }
73 }
74
75 pub fn addr(&self) -> String {
77 format!("{}:{}", self.host, self.port)
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use rskit_validation::Validate;
85
86 #[test]
87 fn default_config_is_valid() {
88 let cfg = GrpcServerConfig::default();
89 assert!(cfg.validate().is_ok());
90 assert_eq!(cfg.host, "0.0.0.0");
91 assert_eq!(cfg.port, 50051);
92 }
93
94 #[test]
95 fn new_sets_host_and_port() {
96 let cfg = GrpcServerConfig::new("127.0.0.1", 8080);
97 assert_eq!(cfg.host, "127.0.0.1");
98 assert_eq!(cfg.port, 8080);
99 assert!(cfg.validate().is_ok());
100 }
101
102 #[test]
103 fn addr_formats_correctly() {
104 let cfg = GrpcServerConfig::new("localhost", 9090);
105 assert_eq!(cfg.addr(), "localhost:9090");
106 }
107
108 #[test]
109 fn empty_host_fails_validation() {
110 let cfg = GrpcServerConfig {
111 host: String::new(),
112 ..Default::default()
113 };
114 assert!(cfg.validate().is_err());
115 }
116
117 #[test]
118 fn zero_port_fails_validation() {
119 let cfg = GrpcServerConfig {
120 port: 0,
121 ..Default::default()
122 };
123 let errors = cfg.validate().unwrap_err();
124 assert!(errors.field_errors().contains_key("port"));
125 }
126
127 #[test]
128 fn tls_config_stores_paths() {
129 let tls = TlsConfig {
130 cert_path: "/certs/server.crt".into(),
131 key_path: "/certs/server.key".into(),
132 };
133 let cfg = GrpcServerConfig {
134 tls: Some(tls.clone()),
135 ..Default::default()
136 };
137 let stored = cfg.tls.unwrap();
138 assert_eq!(stored.cert_path, "/certs/server.crt");
139 assert_eq!(stored.key_path, "/certs/server.key");
140 }
141
142 #[test]
143 fn optional_fields_default_to_none() {
144 let cfg = GrpcServerConfig::default();
145 assert!(cfg.max_connections.is_none());
146 assert!(cfg.keep_alive_secs.is_none());
147 assert!(cfg.tls.is_none());
148 }
149}