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