Skip to main content

defect_cli/
http_stack.rs

1//! Translates the typed HTTP configuration from `defect-config` into a
2//! [`defect_http::HttpStackConfig`].
3//!
4//! `defect-config` avoids a direct dependency on `defect-http` to keep the crate
5//! dependency one-way (see the comment on [`defect_config::HttpClientConfig`]).
6//! Performing this translation during CLI assembly is the most natural place — the same
7//! stack config is shared by three providers, and any proxy URI parsing failures are
8//! reported centrally here.
9
10use std::time::Duration;
11
12use defect_config::{HttpClientConfig, HttpProxyMode, HttpProxySettings};
13
14/// Construct a [`defect_http::HttpStackConfig`] from the typed config.
15///
16/// # Errors
17///
18/// Returns an error when `proxy.mode = Explicit` and the `http_proxy` / `https_proxy` URI
19/// cannot be parsed, to avoid triggering the same error later during provider assembly.
20pub fn build_http_stack_config(
21    config: &HttpClientConfig,
22) -> anyhow::Result<defect_http::HttpStackConfig> {
23    let mut stack = defect_http::HttpStackConfig::default();
24    if let Some(ms) = config.total_timeout_ms {
25        stack.total_timeout = if ms == 0 {
26            None
27        } else {
28            Some(Duration::from_millis(ms))
29        };
30    }
31    if let Some(retries) = config.transport_retries {
32        stack.transport_retries = retries;
33    }
34    if let Some(ms) = config.initial_backoff_ms {
35        stack.initial_backoff = Duration::from_millis(ms);
36    }
37    if let Some(ua) = &config.user_agent {
38        stack.user_agent = Some(ua.clone());
39    }
40    stack.proxy = match config.proxy.mode {
41        HttpProxyMode::FromEnv => defect_http::ProxyConfig::FromEnv,
42        HttpProxyMode::Disabled => defect_http::ProxyConfig::Disabled,
43        HttpProxyMode::Explicit => {
44            defect_http::ProxyConfig::Explicit(parse_proxy_settings(&config.proxy.explicit)?)
45        }
46    };
47    Ok(stack)
48}
49
50fn parse_proxy_settings(
51    settings: &HttpProxySettings,
52) -> anyhow::Result<defect_http::ProxySettings> {
53    let parse_uri = |raw: &str, field: &str| -> anyhow::Result<http::Uri> {
54        raw.parse::<http::Uri>()
55            .map_err(|e| anyhow::anyhow!("invalid http.proxy.{field} `{raw}`: {e}"))
56    };
57    Ok(defect_http::ProxySettings {
58        http_proxy: settings
59            .http_proxy
60            .as_deref()
61            .map(|raw| parse_uri(raw, "http_proxy"))
62            .transpose()?,
63        https_proxy: settings
64            .https_proxy
65            .as_deref()
66            .map(|raw| parse_uri(raw, "https_proxy"))
67            .transpose()?,
68        no_proxy: settings.no_proxy.clone(),
69    })
70}