swan_common/types/
proxy.rs

1use syn::{LitStr, LitBool};
2
3/// 代理类型枚举
4#[derive(Debug, Clone, PartialEq)]
5pub enum ProxyType {
6    /// HTTP/HTTPS 代理
7    Http,
8    /// SOCKS5 代理
9    Socks5,
10}
11
12impl ProxyType {
13    /// 从字符串解析代理类型
14    pub fn from_str(s: &str) -> Option<Self> {
15        match s.to_lowercase().as_str() {
16            "http" => Some(ProxyType::Http),
17            "socks5" => Some(ProxyType::Socks5),
18            _ => None,
19        }
20    }
21
22    /// 转为字符串
23    pub fn as_str(&self) -> &'static str {
24        match self {
25            ProxyType::Http => "http",
26            ProxyType::Socks5 => "socks5",
27        }
28    }
29}
30
31/// 代理配置结构
32/// 
33/// 支持多种代理配置方式:
34/// - 简单 URL 形式:proxy = "http://proxy.example.com:8080"
35/// - 指定类型形式:proxy(type = http, url = "proxy.example.com:8080")
36/// - 完整配置形式:proxy(type = socks5, url = "...", username = "...", password = "...")
37/// - 禁用代理:proxy = false
38#[derive(Clone)]
39pub enum ProxyConfig {
40    /// 简单的代理 URL(从 URL 自动推断类型)
41    Simple(LitStr),
42    /// 完整的代理配置
43    Full {
44        proxy_type: Option<ProxyType>,
45        url: LitStr,
46        username: Option<LitStr>,
47        password: Option<LitStr>,
48        no_proxy: Option<LitStr>,
49    },
50    /// 明确禁用代理
51    Disabled(LitBool),
52}
53
54impl ProxyConfig {
55    /// 获取代理 URL
56    pub fn url(&self) -> Option<&LitStr> {
57        match self {
58            ProxyConfig::Simple(url) => Some(url),
59            ProxyConfig::Full { url, .. } => Some(url),
60            ProxyConfig::Disabled(_) => None,
61        }
62    }
63
64    /// 获取代理类型
65    pub fn proxy_type(&self) -> Option<&ProxyType> {
66        match self {
67            ProxyConfig::Full { proxy_type, .. } => proxy_type.as_ref(),
68            _ => None,
69        }
70    }
71
72    /// 获取用户名
73    pub fn username(&self) -> Option<&LitStr> {
74        match self {
75            ProxyConfig::Full { username, .. } => username.as_ref(),
76            _ => None,
77        }
78    }
79
80    /// 获取密码
81    pub fn password(&self) -> Option<&LitStr> {
82        match self {
83            ProxyConfig::Full { password, .. } => password.as_ref(),
84            _ => None,
85        }
86    }
87
88    /// 获取不使用代理的域名列表
89    pub fn no_proxy(&self) -> Option<&LitStr> {
90        match self {
91            ProxyConfig::Full { no_proxy, .. } => no_proxy.as_ref(),
92            _ => None,
93        }
94    }
95
96    /// 是否明确禁用代理
97    pub fn is_disabled(&self) -> bool {
98        matches!(self, ProxyConfig::Disabled(_))
99    }
100
101    /// 推断代理类型(从 URL 或显式配置)
102    pub fn infer_proxy_type(&self) -> Option<ProxyType> {
103        match self {
104            ProxyConfig::Simple(url) => {
105                let url_value = url.value();
106                if url_value.starts_with("http://") || url_value.starts_with("https://") {
107                    Some(ProxyType::Http)
108                } else if url_value.starts_with("socks5://") {
109                    Some(ProxyType::Socks5)
110                } else {
111                    None
112                }
113            }
114            ProxyConfig::Full { proxy_type, url, .. } => {
115                if let Some(ptype) = proxy_type {
116                    Some(ptype.clone())
117                } else {
118                    // 如果没有显式指定类型,从 URL 推断
119                    let url_value = url.value();
120                    if url_value.starts_with("http://") || url_value.starts_with("https://") {
121                        Some(ProxyType::Http)
122                    } else if url_value.starts_with("socks5://") {
123                        Some(ProxyType::Socks5)
124                    } else {
125                        // 默认为 HTTP
126                        Some(ProxyType::Http)
127                    }
128                }
129            }
130            ProxyConfig::Disabled(_) => None,
131        }
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138    use syn::LitStr;
139    use proc_macro2::Span;
140
141    #[test]
142    fn test_simple_proxy_config() {
143        let url = LitStr::new("http://proxy.example.com:8080", Span::call_site());
144        let config = ProxyConfig::Simple(url);
145        
146        assert!(config.url().is_some());
147        assert_eq!(config.url().unwrap().value(), "http://proxy.example.com:8080");
148        assert!(config.username().is_none());
149        assert!(config.password().is_none());
150        assert!(!config.is_disabled());
151    }
152
153    #[test]
154    fn test_full_proxy_config() {
155        let url = LitStr::new("http://proxy.example.com:8080", Span::call_site());
156        let username = Some(LitStr::new("user", Span::call_site()));
157        let password = Some(LitStr::new("pass", Span::call_site()));
158        
159        let config = ProxyConfig::Full {
160            proxy_type: Some(ProxyType::Http),
161            url,
162            username,
163            password,
164            no_proxy: None,
165        };
166        
167        assert!(config.url().is_some());
168        assert_eq!(config.url().unwrap().value(), "http://proxy.example.com:8080");
169        assert!(config.username().is_some());
170        assert_eq!(config.username().unwrap().value(), "user");
171        assert!(config.password().is_some());
172        assert_eq!(config.password().unwrap().value(), "pass");
173        assert!(!config.is_disabled());
174    }
175
176    #[test]
177    fn test_disabled_proxy_config() {
178        let disabled = LitBool::new(false, Span::call_site());
179        let config = ProxyConfig::Disabled(disabled);
180        
181        assert!(config.url().is_none());
182        assert!(config.username().is_none());
183        assert!(config.password().is_none());
184        assert!(config.is_disabled());
185    }
186}