imp-core 0.2.0

Agent engine for imp: loop, tools, sessions, hooks, context, and SDK
Documentation
pub fn format_error_for_display(raw: &str) -> String {
    let trimmed = raw.trim();
    if trimmed.is_empty() {
        return "Unknown error.".to_string();
    }

    if let Some(message) = extract_json_message(trimmed) {
        return with_auth_hint(&message);
    }

    if looks_like_html_error(trimmed) {
        let status = extract_http_status(trimmed);
        let title = extract_html_title(trimmed);

        return match (status, title) {
            (Some(status), Some(title)) => format!(
                "Provider returned an HTML error page ({status}: {title}). This usually means an auth, gateway, proxy, or rate-limit issue."
            ),
            (Some(status), None) => format!(
                "Provider returned an HTML error page ({status}). This usually means an auth, gateway, proxy, or rate-limit issue."
            ),
            (None, Some(title)) => format!(
                "Provider returned an HTML error page ({title}). This usually means an auth, gateway, proxy, or rate-limit issue."
            ),
            (None, None) => "Provider returned an HTML error page. This usually means an auth, gateway, proxy, or rate-limit issue.".to_string(),
        };
    }

    trimmed.to_string()
}

fn extract_json_message(raw: &str) -> Option<String> {
    let json_start = raw.find('{')?;
    let parsed = serde_json::from_str::<serde_json::Value>(&raw[json_start..]).ok()?;

    parsed
        .get("error")
        .and_then(|error| error.get("message"))
        .and_then(|message| message.as_str())
        .map(ToOwned::to_owned)
        .or_else(|| {
            parsed
                .get("message")
                .and_then(|message| message.as_str())
                .map(ToOwned::to_owned)
        })
}

fn with_auth_hint(message: &str) -> String {
    let lower = message.to_ascii_lowercase();
    let needs_login_hint = lower.contains("expired")
        || lower.contains("oauth")
        || (lower.contains("token")
            && (lower.contains("expired")
                || lower.contains("invalid")
                || lower.contains("refresh")));

    if needs_login_hint {
        format!("{message} (use /login to refresh)")
    } else {
        message.to_string()
    }
}

fn looks_like_html_error(raw: &str) -> bool {
    let lower = raw.to_ascii_lowercase();
    lower.contains("<!doctype html")
        || lower.contains("<html")
        || lower.contains("<head")
        || lower.contains("<body")
        || lower.contains("<title")
}

fn extract_http_status(raw: &str) -> Option<String> {
    let start = raw.find("HTTP ")?;
    let rest = &raw[start..];
    let end = rest.find([':', '\n', '<']).unwrap_or(rest.len());
    let status = rest[..end].trim();
    (!status.is_empty()).then(|| status.to_string())
}

fn extract_html_title(raw: &str) -> Option<String> {
    let lower = raw.to_ascii_lowercase();
    let title_start = lower.find("<title")?;
    let open_end = lower[title_start..].find('>')? + title_start + 1;
    let close_start = lower[open_end..].find("</title>")? + open_end;
    let title = raw[open_end..close_start].trim();
    (!title.is_empty()).then(|| title.to_string())
}

#[cfg(test)]
mod tests {
    use super::format_error_for_display;

    #[test]
    fn extracts_nested_json_error_message() {
        let raw = "Provider error: HTTP 401 Unauthorized: {\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"OAuth token has expired\"}}";
        assert_eq!(
            format_error_for_display(raw),
            "OAuth token has expired (use /login to refresh)"
        );
    }

    #[test]
    fn extracts_simple_json_message() {
        let raw = "Provider error: HTTP 429 Too Many Requests: {\"message\":\"Rate limited\"}";
        assert_eq!(format_error_for_display(raw), "Rate limited");
    }

    #[test]
    fn collapses_html_error_pages_to_summary() {
        let raw = "Provider error: HTTP 403 Forbidden: <!DOCTYPE html><html><head><title>Attention Required! | Cloudflare</title></head><body>blocked</body></html>";
        assert_eq!(
            format_error_for_display(raw),
            "Provider returned an HTML error page (HTTP 403 Forbidden: Attention Required! | Cloudflare). This usually means an auth, gateway, proxy, or rate-limit issue."
        );
    }

    #[test]
    fn leaves_plain_text_errors_alone() {
        let raw = "Provider error: connection reset by peer";
        assert_eq!(format_error_for_display(raw), raw);
    }

    #[test]
    fn replaces_empty_errors_with_unknown_error() {
        assert_eq!(format_error_for_display("   \n"), "Unknown error.");
    }
}