Codex Handbook
feedback/src/feedback_diagnostics.rs 179 lines
use std::collections::HashMap;pub const FEEDBACK_DIAGNOSTICS_ATTACHMENT_FILENAME: &str = "codex-connectivity-diagnostics.txt";const PROXY_ENV_VARS: &[&str] = &[    "HTTP_PROXY",    "http_proxy",    "HTTPS_PROXY",    "https_proxy",    "ALL_PROXY",    "all_proxy",];#[derive(Debug, Clone, Default, PartialEq, Eq)]pub struct FeedbackDiagnostics {    diagnostics: Vec<FeedbackDiagnostic>,}#[derive(Debug, Clone, PartialEq, Eq)]pub struct FeedbackDiagnostic {    pub headline: String,    pub details: Vec<String>,}impl FeedbackDiagnostics {    pub fn new(diagnostics: Vec<FeedbackDiagnostic>) -> Self {        Self { diagnostics }    }    pub fn collect_from_env() -> Self {        Self::collect_from_pairs(std::env::vars())    }    fn collect_from_pairs<I, K, V>(pairs: I) -> Self    where        I: IntoIterator<Item = (K, V)>,        K: Into<String>,        V: Into<String>,    {        let env = pairs            .into_iter()            .map(|(key, value)| (key.into(), value.into()))            .collect::<HashMap<_, _>>();        let mut diagnostics = Vec::new();        let proxy_details = PROXY_ENV_VARS            .iter()            .filter_map(|key| {                let value = env.get(*key)?;                Some(format!("{key} = {value}"))            })            .collect::<Vec<_>>();        if !proxy_details.is_empty() {            diagnostics.push(FeedbackDiagnostic {                headline: "Proxy environment variables are set and may affect connectivity."                    .to_string(),                details: proxy_details,            });        }        Self { diagnostics }    }    pub fn is_empty(&self) -> bool {        self.diagnostics.is_empty()    }    pub fn diagnostics(&self) -> &[FeedbackDiagnostic] {        &self.diagnostics    }    pub fn attachment_text(&self) -> Option<String> {        if self.diagnostics.is_empty() {            return None;        }        let mut lines = vec!["Connectivity diagnostics".to_string(), String::new()];        for diagnostic in &self.diagnostics {            lines.push(format!("- {}", diagnostic.headline));            lines.extend(                diagnostic                    .details                    .iter()                    .map(|detail| format!("  - {detail}")),            );        }        Some(lines.join("\n"))    }}#[cfg(test)]mod tests {    use pretty_assertions::assert_eq;    use super::FeedbackDiagnostic;    use super::FeedbackDiagnostics;    #[test]    fn collect_from_pairs_reports_raw_values_and_attachment() {        let diagnostics = FeedbackDiagnostics::collect_from_pairs([            (                "HTTPS_PROXY",                "https://user:password@secure-proxy.example.com:443?secret=1",            ),            ("http_proxy", "proxy.example.com:8080"),            ("all_proxy", "socks5h://all-proxy.example.com:1080"),        ]);        assert_eq!(            diagnostics,            FeedbackDiagnostics {                diagnostics: vec![FeedbackDiagnostic {                    headline: "Proxy environment variables are set and may affect connectivity."                        .to_string(),                    details: vec![                        "http_proxy = proxy.example.com:8080".to_string(),                        "HTTPS_PROXY = https://user:password@secure-proxy.example.com:443?secret=1"                            .to_string(),                        "all_proxy = socks5h://all-proxy.example.com:1080".to_string(),                    ],                },],            }        );        assert_eq!(            diagnostics.attachment_text(),            Some(                r#"Connectivity diagnostics- Proxy environment variables are set and may affect connectivity.  - http_proxy = proxy.example.com:8080  - HTTPS_PROXY = https://user:password@secure-proxy.example.com:443?secret=1  - all_proxy = socks5h://all-proxy.example.com:1080"#                    .to_string()            )        );    }    #[test]    fn collect_from_pairs_ignores_absent_values() {        let diagnostics = FeedbackDiagnostics::collect_from_pairs(Vec::<(String, String)>::new());        assert_eq!(diagnostics, FeedbackDiagnostics::default());        assert_eq!(diagnostics.attachment_text(), None);    }    #[test]    fn collect_from_pairs_preserves_whitespace_and_empty_values() {        let diagnostics =            FeedbackDiagnostics::collect_from_pairs([("HTTP_PROXY", "  proxy with spaces  ")]);        assert_eq!(            diagnostics,            FeedbackDiagnostics {                diagnostics: vec![FeedbackDiagnostic {                    headline: "Proxy environment variables are set and may affect connectivity."                        .to_string(),                    details: vec!["HTTP_PROXY =   proxy with spaces  ".to_string()],                },],            }        );    }    #[test]    fn collect_from_pairs_reports_values_verbatim() {        let proxy_value = "not a valid proxy";        let diagnostics = FeedbackDiagnostics::collect_from_pairs([("HTTP_PROXY", proxy_value)]);        assert_eq!(            diagnostics,            FeedbackDiagnostics {                diagnostics: vec![FeedbackDiagnostic {                    headline: "Proxy environment variables are set and may affect connectivity."                        .to_string(),                    details: vec!["HTTP_PROXY = not a valid proxy".to_string()],                },],            }        );    }}