use rskit_validation::Validate;
use serde::{Deserialize, Serialize};
use validator::{ValidationError, ValidationErrors};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TlsConfig {
pub cert_path: String,
pub key_path: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct GrpcServerConfig {
pub host: String,
pub port: u16,
pub max_connections: Option<usize>,
pub keep_alive_secs: Option<u64>,
pub tls: Option<TlsConfig>,
}
impl Validate for GrpcServerConfig {
fn validate(&self) -> Result<(), ValidationErrors> {
let mut errors = ValidationErrors::new();
if self.host.trim().is_empty() {
errors.add("host", ValidationError::new("length"));
}
if self.port == 0 {
errors.add("port", ValidationError::new("range"));
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
}
impl Default for GrpcServerConfig {
fn default() -> Self {
Self {
host: "0.0.0.0".into(),
port: 50051,
max_connections: None,
keep_alive_secs: None,
tls: None,
}
}
}
impl GrpcServerConfig {
pub fn new(host: impl Into<String>, port: u16) -> Self {
Self {
host: host.into(),
port,
..Default::default()
}
}
pub fn addr(&self) -> String {
format!("{}:{}", self.host, self.port)
}
}
#[cfg(test)]
mod tests {
use super::*;
use rskit_validation::Validate;
#[test]
fn default_config_is_valid() {
let cfg = GrpcServerConfig::default();
assert!(cfg.validate().is_ok());
assert_eq!(cfg.host, "0.0.0.0");
assert_eq!(cfg.port, 50051);
}
#[test]
fn new_sets_host_and_port() {
let cfg = GrpcServerConfig::new("127.0.0.1", 8080);
assert_eq!(cfg.host, "127.0.0.1");
assert_eq!(cfg.port, 8080);
assert!(cfg.validate().is_ok());
}
#[test]
fn addr_formats_correctly() {
let cfg = GrpcServerConfig::new("localhost", 9090);
assert_eq!(cfg.addr(), "localhost:9090");
}
#[test]
fn empty_host_fails_validation() {
let cfg = GrpcServerConfig {
host: String::new(),
..Default::default()
};
assert!(cfg.validate().is_err());
}
#[test]
fn zero_port_fails_validation() {
let cfg = GrpcServerConfig {
port: 0,
..Default::default()
};
let errors = cfg.validate().unwrap_err();
assert!(errors.field_errors().contains_key("port"));
}
#[test]
fn tls_config_stores_paths() {
let tls = TlsConfig {
cert_path: "/certs/server.crt".into(),
key_path: "/certs/server.key".into(),
};
let cfg = GrpcServerConfig {
tls: Some(tls.clone()),
..Default::default()
};
let stored = cfg.tls.unwrap();
assert_eq!(stored.cert_path, "/certs/server.crt");
assert_eq!(stored.key_path, "/certs/server.key");
}
#[test]
fn optional_fields_default_to_none() {
let cfg = GrpcServerConfig::default();
assert!(cfg.max_connections.is_none());
assert!(cfg.keep_alive_secs.is_none());
assert!(cfg.tls.is_none());
}
}