use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ProxyType {
Http,
Https,
Socks4,
#[serde(rename = "socks")]
Socks5,
#[default]
Direct,
}
impl ProxyType {
#[inline]
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Http => "http",
Self::Https => "https",
Self::Socks4 => "socks4",
Self::Socks5 => "socks",
Self::Direct => "direct",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProxyConfig {
#[serde(rename = "type")]
pub proxy_type: ProxyType,
pub host: String,
pub port: u16,
#[serde(skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub password: Option<String>,
#[serde(rename = "proxyDns", default)]
pub proxy_dns: bool,
}
impl ProxyConfig {
#[must_use]
pub fn new(proxy_type: ProxyType, host: impl Into<String>, port: u16) -> Self {
Self {
proxy_type,
host: host.into(),
port,
username: None,
password: None,
proxy_dns: false,
}
}
#[inline]
#[must_use]
pub fn http(host: impl Into<String>, port: u16) -> Self {
Self::new(ProxyType::Http, host, port)
}
#[inline]
#[must_use]
pub fn https(host: impl Into<String>, port: u16) -> Self {
Self::new(ProxyType::Https, host, port)
}
#[inline]
#[must_use]
pub fn socks4(host: impl Into<String>, port: u16) -> Self {
Self::new(ProxyType::Socks4, host, port)
}
#[inline]
#[must_use]
pub fn socks5(host: impl Into<String>, port: u16) -> Self {
Self {
proxy_type: ProxyType::Socks5,
host: host.into(),
port,
username: None,
password: None,
proxy_dns: true, }
}
#[inline]
#[must_use]
pub fn direct() -> Self {
Self {
proxy_type: ProxyType::Direct,
host: String::new(),
port: 0,
username: None,
password: None,
proxy_dns: false,
}
}
}
impl ProxyConfig {
#[must_use]
pub fn with_credentials(
mut self,
username: impl Into<String>,
password: impl Into<String>,
) -> Self {
self.username = Some(username.into());
self.password = Some(password.into());
self
}
#[must_use]
pub fn with_proxy_dns(mut self, proxy_dns: bool) -> Self {
self.proxy_dns = proxy_dns;
self
}
}
impl ProxyConfig {
#[inline]
#[must_use]
pub fn has_auth(&self) -> bool {
self.username.is_some() && self.password.is_some()
}
#[inline]
#[must_use]
pub fn is_socks(&self) -> bool {
matches!(self.proxy_type, ProxyType::Socks4 | ProxyType::Socks5)
}
#[inline]
#[must_use]
pub fn is_http(&self) -> bool {
matches!(self.proxy_type, ProxyType::Http | ProxyType::Https)
}
}
#[cfg(test)]
mod tests {
use super::{ProxyConfig, ProxyType};
#[test]
fn test_proxy_type_as_str() {
assert_eq!(ProxyType::Http.as_str(), "http");
assert_eq!(ProxyType::Https.as_str(), "https");
assert_eq!(ProxyType::Socks4.as_str(), "socks4");
assert_eq!(ProxyType::Socks5.as_str(), "socks");
assert_eq!(ProxyType::Direct.as_str(), "direct");
}
#[test]
fn test_proxy_type_serialization() {
assert_eq!(
serde_json::to_string(&ProxyType::Http).unwrap(),
r#""http""#
);
assert_eq!(
serde_json::to_string(&ProxyType::Https).unwrap(),
r#""https""#
);
assert_eq!(
serde_json::to_string(&ProxyType::Socks4).unwrap(),
r#""socks4""#
);
assert_eq!(
serde_json::to_string(&ProxyType::Socks5).unwrap(),
r#""socks""#
);
assert_eq!(
serde_json::to_string(&ProxyType::Direct).unwrap(),
r#""direct""#
);
}
#[test]
fn test_proxy_type_default() {
assert_eq!(ProxyType::default(), ProxyType::Direct);
}
#[test]
fn test_proxy_config_http() {
let proxy = ProxyConfig::http("proxy.example.com", 8080);
assert_eq!(proxy.proxy_type, ProxyType::Http);
assert_eq!(proxy.host, "proxy.example.com");
assert_eq!(proxy.port, 8080);
assert!(!proxy.has_auth());
assert!(proxy.is_http());
assert!(!proxy.is_socks());
}
#[test]
fn test_proxy_config_https() {
let proxy = ProxyConfig::https("proxy.example.com", 8443);
assert_eq!(proxy.proxy_type, ProxyType::Https);
assert!(proxy.is_http());
}
#[test]
fn test_proxy_config_socks4() {
let proxy = ProxyConfig::socks4("proxy.example.com", 1080);
assert_eq!(proxy.proxy_type, ProxyType::Socks4);
assert!(proxy.is_socks());
assert!(!proxy.is_http());
}
#[test]
fn test_proxy_config_socks5() {
let proxy = ProxyConfig::socks5("proxy.example.com", 1080);
assert_eq!(proxy.proxy_type, ProxyType::Socks5);
assert!(proxy.is_socks());
assert!(proxy.proxy_dns); }
#[test]
fn test_proxy_config_direct() {
let proxy = ProxyConfig::direct();
assert_eq!(proxy.proxy_type, ProxyType::Direct);
assert!(!proxy.is_http());
assert!(!proxy.is_socks());
}
#[test]
fn test_proxy_config_with_auth() {
let proxy = ProxyConfig::socks5("proxy.example.com", 1080)
.with_credentials("user", "pass")
.with_proxy_dns(true);
assert_eq!(proxy.proxy_type, ProxyType::Socks5);
assert!(proxy.has_auth());
assert!(proxy.is_socks());
assert!(proxy.proxy_dns);
assert_eq!(proxy.username.as_deref(), Some("user"));
assert_eq!(proxy.password.as_deref(), Some("pass"));
}
#[test]
fn test_proxy_config_serialization() {
let proxy = ProxyConfig::http("proxy.example.com", 8080).with_credentials("user", "pass");
let json = serde_json::to_string(&proxy).unwrap();
assert!(json.contains(r#""type":"http""#));
assert!(json.contains(r#""host":"proxy.example.com""#));
assert!(json.contains(r#""port":8080"#));
assert!(json.contains(r#""username":"user""#));
}
#[test]
fn test_proxy_config_clone() {
let proxy = ProxyConfig::http("proxy.example.com", 8080);
let cloned = proxy.clone();
assert_eq!(proxy.host, cloned.host);
assert_eq!(proxy.port, cloned.port);
}
}