use std::time::Duration;
use crate::error::{ConsulError, Result};
pub const DEFAULT_HTTP_PORT: u16 = 8500;
pub const DEFAULT_ADDRESS: &str = "127.0.0.1:8500";
pub const DEFAULT_SCHEME: &str = "http";
#[derive(Clone, Debug, Default)]
pub struct TlsConfig {
pub ca_cert: Option<String>,
pub ca_cert_pem: Option<Vec<u8>>,
pub client_cert: Option<String>,
pub client_cert_pem: Option<Vec<u8>>,
pub client_key: Option<String>,
pub client_key_pem: Option<Vec<u8>>,
pub insecure_skip_verify: bool,
}
impl TlsConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_ca_cert(mut self, path: &str) -> Self {
self.ca_cert = Some(path.to_string());
self
}
pub fn with_client_cert(mut self, cert_path: &str, key_path: &str) -> Self {
self.client_cert = Some(cert_path.to_string());
self.client_key = Some(key_path.to_string());
self
}
pub fn with_insecure_skip_verify(mut self) -> Self {
self.insecure_skip_verify = true;
self
}
}
#[derive(Clone, Debug)]
pub struct HttpBasicAuth {
pub username: String,
pub password: String,
}
impl HttpBasicAuth {
pub fn new(username: &str, password: &str) -> Self {
Self {
username: username.to_string(),
password: password.to_string(),
}
}
}
#[derive(Clone, Debug)]
pub struct Config {
pub address: String,
pub scheme: String,
pub datacenter: Option<String>,
pub http_auth: Option<HttpBasicAuth>,
pub token: Option<String>,
pub token_file: Option<String>,
pub tls_config: Option<TlsConfig>,
pub timeout: Duration,
pub namespace: Option<String>,
pub partition: Option<String>,
}
impl Default for Config {
fn default() -> Self {
Self {
address: DEFAULT_ADDRESS.to_string(),
scheme: DEFAULT_SCHEME.to_string(),
datacenter: None,
http_auth: None,
token: None,
token_file: None,
tls_config: None,
timeout: Duration::from_secs(30),
namespace: None,
partition: None,
}
}
}
impl Config {
pub fn new() -> Self {
Self::default()
}
pub fn from_env() -> Result<Self> {
let mut config = Self::default();
if let Ok(addr) = std::env::var("CONSUL_HTTP_ADDR") {
if addr.starts_with("https://") {
config.scheme = "https".to_string();
config.address = addr.trim_start_matches("https://").to_string();
} else if addr.starts_with("http://") {
config.scheme = "http".to_string();
config.address = addr.trim_start_matches("http://").to_string();
} else {
config.address = addr;
}
}
if let Ok(token) = std::env::var("CONSUL_HTTP_TOKEN") {
config.token = Some(token);
}
if let Ok(token_file) = std::env::var("CONSUL_HTTP_TOKEN_FILE") {
config.token_file = Some(token_file);
}
if let Ok(auth) = std::env::var("CONSUL_HTTP_AUTH") {
if let Some((username, password)) = auth.split_once(':') {
config.http_auth = Some(HttpBasicAuth::new(username, password));
}
}
if let Ok(ssl) = std::env::var("CONSUL_HTTP_SSL") {
if ssl == "true" || ssl == "1" {
config.scheme = "https".to_string();
}
}
if let Ok(verify) = std::env::var("CONSUL_HTTP_SSL_VERIFY") {
if verify == "false" || verify == "0" {
let mut tls = config.tls_config.unwrap_or_default();
tls.insecure_skip_verify = true;
config.tls_config = Some(tls);
}
}
if let Ok(ca_cert) = std::env::var("CONSUL_CACERT") {
let mut tls = config.tls_config.unwrap_or_default();
tls.ca_cert = Some(ca_cert);
config.tls_config = Some(tls);
}
if let Ok(client_cert) = std::env::var("CONSUL_CLIENT_CERT") {
let mut tls = config.tls_config.unwrap_or_default();
tls.client_cert = Some(client_cert);
config.tls_config = Some(tls);
}
if let Ok(client_key) = std::env::var("CONSUL_CLIENT_KEY") {
let mut tls = config.tls_config.unwrap_or_default();
tls.client_key = Some(client_key);
config.tls_config = Some(tls);
}
if let Ok(ns) = std::env::var("CONSUL_NAMESPACE") {
config.namespace = Some(ns);
}
if let Ok(partition) = std::env::var("CONSUL_PARTITION") {
config.partition = Some(partition);
}
Ok(config)
}
pub fn base_url(&self) -> String {
format!("{}://{}", self.scheme, self.address)
}
pub fn validate(&self) -> Result<()> {
if self.address.is_empty() {
return Err(ConsulError::InvalidConfig("address is required".to_string()));
}
if self.scheme != "http" && self.scheme != "https" {
return Err(ConsulError::InvalidConfig(format!(
"invalid scheme: {}",
self.scheme
)));
}
Ok(())
}
}
pub struct ConfigBuilder {
config: Config,
}
impl ConfigBuilder {
pub fn new() -> Self {
Self {
config: Config::default(),
}
}
pub fn address(mut self, address: &str) -> Self {
self.config.address = address.to_string();
self
}
pub fn scheme(mut self, scheme: &str) -> Self {
self.config.scheme = scheme.to_string();
self
}
pub fn datacenter(mut self, dc: &str) -> Self {
self.config.datacenter = Some(dc.to_string());
self
}
pub fn token(mut self, token: &str) -> Self {
self.config.token = Some(token.to_string());
self
}
pub fn http_auth(mut self, username: &str, password: &str) -> Self {
self.config.http_auth = Some(HttpBasicAuth::new(username, password));
self
}
pub fn tls_config(mut self, tls: TlsConfig) -> Self {
self.config.tls_config = Some(tls);
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.config.timeout = timeout;
self
}
pub fn namespace(mut self, ns: &str) -> Self {
self.config.namespace = Some(ns.to_string());
self
}
pub fn partition(mut self, partition: &str) -> Self {
self.config.partition = Some(partition.to_string());
self
}
pub fn build(self) -> Result<Config> {
self.config.validate()?;
Ok(self.config)
}
}
impl Default for ConfigBuilder {
fn default() -> Self {
Self::new()
}
}