qubit_http/options/
proxy_options.rs1use std::fmt;
12use std::str::FromStr;
13
14use qubit_config::{
15 ConfigReader,
16 ConfigResult,
17};
18
19use super::HttpConfigError;
20
21use super::proxy_type::ProxyType;
22
23#[derive(Clone, PartialEq, Eq)]
25pub struct ProxyOptions {
26 pub enabled: bool,
28 pub proxy_type: ProxyType,
30 pub host: Option<String>,
32 pub port: Option<u16>,
34 pub username: Option<String>,
36 pub password: Option<String>,
38}
39
40impl fmt::Debug for ProxyOptions {
41 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
42 let password = self.password.as_ref().map(|_| "****");
43 formatter
44 .debug_struct("ProxyOptions")
45 .field("enabled", &self.enabled)
46 .field("proxy_type", &self.proxy_type)
47 .field("host", &self.host)
48 .field("port", &self.port)
49 .field("username", &self.username)
50 .field("password", &password)
51 .finish()
52 }
53}
54
55impl Default for ProxyOptions {
56 fn default() -> Self {
61 Self {
62 enabled: false,
63 proxy_type: ProxyType::Http,
64 host: None,
65 port: None,
66 username: None,
67 password: None,
68 }
69 }
70}
71
72struct ProxyConfigInput {
73 enabled: Option<bool>,
74 proxy_type: Option<String>,
75 host: Option<String>,
76 port: Option<u16>,
77 username: Option<String>,
78 password: Option<String>,
79}
80
81fn read_proxy_config<R>(config: &R) -> ConfigResult<ProxyConfigInput>
82where
83 R: ConfigReader + ?Sized,
84{
85 Ok(ProxyConfigInput {
86 enabled: config.get_optional("enabled")?,
87 proxy_type: config.get_optional_string("proxy_type")?,
88 host: config.get_optional_string("host")?,
89 port: config.get_optional("port")?,
90 username: config.get_optional_string("username")?,
91 password: config.get_optional_string("password")?,
92 })
93}
94
95impl ProxyOptions {
96 pub fn from_config<R>(config: &R) -> Result<Self, HttpConfigError>
112 where
113 R: ConfigReader + ?Sized,
114 {
115 let raw = read_proxy_config(config).map_err(HttpConfigError::from)?;
116
117 let mut opts = ProxyOptions::default();
118 if let Some(v) = raw.enabled {
119 opts.enabled = v;
120 }
121 if let Some(s) = raw.proxy_type {
122 opts.proxy_type = parse_proxy_type("proxy_type", &s)?;
123 }
124 opts.host = raw.host;
125 if let Some(p) = raw.port {
126 opts.port = Some(p);
127 }
128 opts.username = raw.username;
129 opts.password = raw.password;
130
131 Ok(opts)
132 }
133
134 pub fn validate(&self) -> Result<(), HttpConfigError> {
140 if self.enabled {
141 match self.host.as_deref() {
142 None => {
143 return Err(HttpConfigError::missing(
144 "proxy.host",
145 "Proxy is enabled but host is missing",
146 ));
147 }
148 Some(host) if host.trim().is_empty() => {
149 return Err(HttpConfigError::invalid_value(
150 "proxy.host",
151 "Proxy host cannot be empty when proxy is enabled",
152 ));
153 }
154 _ => {}
155 }
156 match self.port {
157 None => {
158 return Err(HttpConfigError::missing(
159 "proxy.port",
160 "Proxy is enabled but port is missing",
161 ));
162 }
163 Some(0) => {
164 return Err(HttpConfigError::invalid_value(
165 "proxy.port",
166 "Proxy port must be greater than 0",
167 ));
168 }
169 _ => {}
170 }
171 }
172 if let Some(username) = self.username.as_deref() {
173 if username.trim().is_empty() {
174 return Err(HttpConfigError::invalid_value(
175 "proxy.username",
176 "Proxy username cannot be empty when provided",
177 ));
178 }
179 }
180 if self.username.is_none() && self.password.is_some() {
181 return Err(HttpConfigError::missing(
182 "proxy.username",
183 "Proxy password is configured but username is missing",
184 ));
185 }
186 Ok(())
187 }
188}
189
190fn parse_proxy_type(path: &str, s: &str) -> Result<ProxyType, HttpConfigError> {
191 ProxyType::from_str(s.trim()).map_err(|_| {
192 HttpConfigError::invalid_value(
193 path,
194 format!("Unknown proxy type '{}'; expected http, https, or socks5", s,),
195 )
196 })
197}