use std::path::PathBuf;
use serde::Deserialize;
pub const DEFAULT_URL: &str = "nats://127.0.0.1:4222";
pub const DEFAULT_MAX_INFLIGHT: usize = 1_024;
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq)]
#[serde(default)]
pub struct NatsTlsConfig {
pub ca: Option<PathBuf>,
pub cert: Option<PathBuf>,
pub key: Option<PathBuf>,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[serde(default)]
pub struct NatsConfig {
pub url: String,
pub token: Option<String>,
pub tls: Option<NatsTlsConfig>,
pub max_inflight: usize,
}
impl Default for NatsConfig {
fn default() -> Self {
Self {
url: DEFAULT_URL.to_string(),
token: None,
tls: None,
max_inflight: DEFAULT_MAX_INFLIGHT,
}
}
}
#[derive(Debug, Default, Deserialize)]
#[serde(default)]
struct GatewaySection {
nats: NatsConfig,
}
#[derive(Debug, Default, Deserialize)]
#[serde(default)]
struct ConfigRoot {
gateway: GatewaySection,
}
impl NatsConfig {
pub fn from_toml_str(toml: &str) -> Result<Self, toml::de::Error> {
Ok(toml::from_str::<ConfigRoot>(toml)?.gateway.nats)
}
#[must_use]
pub fn connect_options(&self) -> async_nats::ConnectOptions {
let mut opts = async_nats::ConnectOptions::new().client_capacity(self.max_inflight.max(1));
if let Some(token) = &self.token {
opts = opts.token(token.clone());
}
if let Some(tls) = &self.tls {
opts = opts.require_tls(true);
if let Some(ca) = &tls.ca {
opts = opts.add_root_certificates(ca.clone());
}
if let (Some(cert), Some(key)) = (&tls.cert, &tls.key) {
opts = opts.add_client_certificate(cert.clone(), key.clone());
}
}
opts
}
pub async fn connect(&self) -> Result<async_nats::Client, async_nats::ConnectError> {
self.connect_options().connect(self.url.clone()).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_full_gateway_nats_table() {
let toml = r#"
[gateway.nats]
url = "tls://nats.example.com:4222"
token = "s3cr3t"
max_inflight = 4096
[gateway.nats.tls]
ca = "/etc/aa/ca.pem"
cert = "/etc/aa/client.pem"
key = "/etc/aa/client.key"
"#;
let cfg = NatsConfig::from_toml_str(toml).expect("valid config");
assert_eq!(cfg.url, "tls://nats.example.com:4222");
assert_eq!(cfg.token.as_deref(), Some("s3cr3t"));
assert_eq!(cfg.max_inflight, 4096);
let tls = cfg.tls.expect("tls table present");
assert_eq!(tls.ca, Some(PathBuf::from("/etc/aa/ca.pem")));
assert_eq!(tls.cert, Some(PathBuf::from("/etc/aa/client.pem")));
assert_eq!(tls.key, Some(PathBuf::from("/etc/aa/client.key")));
}
#[test]
fn falls_back_to_defaults_when_table_absent() {
let cfg = NatsConfig::from_toml_str("[storage]\naudit_sink = \"postgres\"\n").expect("valid config");
assert_eq!(cfg, NatsConfig::default());
assert_eq!(cfg.url, DEFAULT_URL);
assert_eq!(cfg.max_inflight, DEFAULT_MAX_INFLIGHT);
assert!(cfg.token.is_none());
assert!(cfg.tls.is_none());
}
}