qubit_http/options/
proxy_options.rs1use qubit_config::{
12 ConfigReader,
13 ConfigResult,
14};
15
16use super::HttpConfigError;
17
18use super::proxy_type::ProxyType;
19use std::str::FromStr;
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct ProxyOptions {
24 pub enabled: bool,
26 pub proxy_type: ProxyType,
28 pub host: Option<String>,
30 pub port: Option<u16>,
32 pub username: Option<String>,
34 pub password: Option<String>,
36}
37
38impl Default for ProxyOptions {
39 fn default() -> Self {
44 Self {
45 enabled: false,
46 proxy_type: ProxyType::Http,
47 host: None,
48 port: None,
49 username: None,
50 password: None,
51 }
52 }
53}
54
55struct ProxyConfigInput {
56 enabled: Option<bool>,
57 proxy_type: Option<String>,
58 host: Option<String>,
59 port: Option<u16>,
60 username: Option<String>,
61 password: Option<String>,
62}
63
64fn read_proxy_config<R>(config: &R) -> ConfigResult<ProxyConfigInput>
65where
66 R: ConfigReader + ?Sized,
67{
68 Ok(ProxyConfigInput {
69 enabled: config.get_optional("enabled")?,
70 proxy_type: config.get_optional_string("proxy_type")?,
71 host: config.get_optional_string("host")?,
72 port: config.get_optional("port")?,
73 username: config.get_optional_string("username")?,
74 password: config.get_optional_string("password")?,
75 })
76}
77
78impl ProxyOptions {
79 pub fn from_config<R>(config: &R) -> Result<Self, HttpConfigError>
95 where
96 R: ConfigReader + ?Sized,
97 {
98 let raw = read_proxy_config(config).map_err(HttpConfigError::from)?;
99
100 let mut opts = ProxyOptions::default();
101 if let Some(v) = raw.enabled {
102 opts.enabled = v;
103 }
104 if let Some(s) = raw.proxy_type {
105 opts.proxy_type = parse_proxy_type("proxy_type", &s)?;
106 }
107 opts.host = raw.host;
108 if let Some(p) = raw.port {
109 opts.port = Some(p);
110 }
111 opts.username = raw.username;
112 opts.password = raw.password;
113
114 Ok(opts)
115 }
116
117 pub fn validate(&self) -> Result<(), HttpConfigError> {
123 if self.enabled {
124 match self.host.as_deref() {
125 None => {
126 return Err(HttpConfigError::missing(
127 "proxy.host",
128 "Proxy is enabled but host is missing",
129 ));
130 }
131 Some(host) if host.trim().is_empty() => {
132 return Err(HttpConfigError::invalid_value(
133 "proxy.host",
134 "Proxy host cannot be empty when proxy is enabled",
135 ));
136 }
137 _ => {}
138 }
139 match self.port {
140 None => {
141 return Err(HttpConfigError::missing(
142 "proxy.port",
143 "Proxy is enabled but port is missing",
144 ));
145 }
146 Some(0) => {
147 return Err(HttpConfigError::invalid_value(
148 "proxy.port",
149 "Proxy port must be greater than 0",
150 ));
151 }
152 _ => {}
153 }
154 }
155 if let Some(username) = self.username.as_deref() {
156 if username.trim().is_empty() {
157 return Err(HttpConfigError::invalid_value(
158 "proxy.username",
159 "Proxy username cannot be empty when provided",
160 ));
161 }
162 }
163 if self.username.is_none() && self.password.is_some() {
164 return Err(HttpConfigError::missing(
165 "proxy.username",
166 "Proxy password is configured but username is missing",
167 ));
168 }
169 Ok(())
170 }
171}
172
173fn parse_proxy_type(path: &str, s: &str) -> Result<ProxyType, HttpConfigError> {
174 ProxyType::from_str(s.trim()).map_err(|_| {
175 HttpConfigError::invalid_value(
176 path,
177 format!(
178 "Unknown proxy type '{}'; expected http, https, or socks5",
179 s,
180 ),
181 )
182 })
183}