use reqwest;
pub(super) const ERROR_BODY_PREVIEW_BYTES: usize = 200;
pub(super) fn mask_url_secrets(url: &str) -> String {
if let Ok(parsed) = reqwest::Url::parse(url) {
let mut clone = parsed.clone();
if !parsed.username().is_empty() || parsed.password().is_some() {
let _ = clone.set_username("***");
let _ = clone.set_password(Some("***"));
}
return clone.to_string();
}
url.to_string()
}
pub(super) fn redact_body_preview(body: &str) -> String {
let mut out = body.to_string();
if let Some(idx) = out.to_lowercase().find("bearer ") {
let tail_start = idx + "bearer ".len();
if tail_start < out.len() {
let end = out[tail_start..]
.find(|c: char| c.is_whitespace() || c == '"' || c == ',')
.map_or(out.len(), |off| tail_start + off);
out.replace_range(tail_start..end, "***");
}
}
for needle in ["api_key=", "apikey=", "api-key=", "token="] {
if let Some(idx) = out.to_lowercase().find(needle) {
let tail_start = idx + needle.len();
let end = out[tail_start..]
.find(|c: char| c.is_whitespace() || c == '&' || c == '"' || c == ',')
.map_or(out.len(), |off| tail_start + off);
out.replace_range(tail_start..end, "***");
}
}
out
}
pub(super) async fn bounded_body_excerpt(response: reqwest::Response, max_bytes: usize) -> String {
let body_text = response.text().await.unwrap_or_default();
if body_text.is_empty() {
return "<no body>".to_string();
}
let trimmed: String = body_text.chars().take(max_bytes).collect();
let suffix = if body_text.len() > trimmed.len() {
"…"
} else {
""
};
let one_line = trimmed.replace(['\n', '\r'], " ");
format!("{}{}", redact_body_preview(&one_line), suffix)
}