use qubit_config::{ConfigReader, ConfigResult};
use super::HttpConfigError;
use super::proxy_type::ProxyType;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProxyOptions {
pub enabled: bool,
pub proxy_type: ProxyType,
pub host: Option<String>,
pub port: Option<u16>,
pub username: Option<String>,
pub password: Option<String>,
}
impl Default for ProxyOptions {
fn default() -> Self {
Self {
enabled: false,
proxy_type: ProxyType::Http,
host: None,
port: None,
username: None,
password: None,
}
}
}
struct ProxyConfigInput {
enabled: Option<bool>,
proxy_type: Option<String>,
host: Option<String>,
port: Option<u16>,
username: Option<String>,
password: Option<String>,
}
fn read_proxy_config<R>(config: &R) -> ConfigResult<ProxyConfigInput>
where
R: ConfigReader + ?Sized,
{
Ok(ProxyConfigInput {
enabled: config.get_optional("enabled")?,
proxy_type: config.get_optional_string("proxy_type")?,
host: config.get_optional_string("host")?,
port: config.get_optional("port")?,
username: config.get_optional_string("username")?,
password: config.get_optional_string("password")?,
})
}
impl ProxyOptions {
pub fn from_config<R>(config: &R) -> Result<Self, HttpConfigError>
where
R: ConfigReader + ?Sized,
{
let raw = read_proxy_config(config).map_err(HttpConfigError::from)?;
let mut opts = ProxyOptions::default();
if let Some(v) = raw.enabled {
opts.enabled = v;
}
if let Some(s) = raw.proxy_type {
opts.proxy_type = parse_proxy_type("proxy_type", &s)?;
}
opts.host = raw.host;
if let Some(p) = raw.port {
opts.port = Some(p);
}
opts.username = raw.username;
opts.password = raw.password;
Ok(opts)
}
pub fn validate(&self) -> Result<(), HttpConfigError> {
if self.enabled {
match self.host.as_deref() {
None => {
return Err(HttpConfigError::missing(
"proxy.host",
"Proxy is enabled but host is missing",
));
}
Some(host) if host.trim().is_empty() => {
return Err(HttpConfigError::invalid_value(
"proxy.host",
"Proxy host cannot be empty when proxy is enabled",
));
}
_ => {}
}
match self.port {
None => {
return Err(HttpConfigError::missing(
"proxy.port",
"Proxy is enabled but port is missing",
));
}
Some(0) => {
return Err(HttpConfigError::invalid_value(
"proxy.port",
"Proxy port must be greater than 0",
));
}
_ => {}
}
}
if let Some(username) = self.username.as_deref() {
if username.trim().is_empty() {
return Err(HttpConfigError::invalid_value(
"proxy.username",
"Proxy username cannot be empty when provided",
));
}
}
if self.username.is_none() && self.password.is_some() {
return Err(HttpConfigError::missing(
"proxy.username",
"Proxy password is configured but username is missing",
));
}
Ok(())
}
}
fn parse_proxy_type(path: &str, s: &str) -> Result<ProxyType, HttpConfigError> {
ProxyType::from_str(s.trim()).map_err(|_| {
HttpConfigError::invalid_value(
path,
format!(
"Unknown proxy type '{}'; expected http, https, or socks5",
s,
),
)
})
}