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