Skip to main content

docs_mcp/docsrs/
client.rs

1use reqwest_middleware::ClientWithMiddleware;
2
3use crate::cache::DiskCache;
4use crate::error::{DocsError, Result};
5use super::types::RustdocJson;
6
7const DOCSRS_BASE: &str = "https://docs.rs";
8
9/// Fetch the rustdoc JSON for a crate from docs.rs.
10///
11/// Returns `Err(DocsError::DocsNotFound)` if docs.rs has no successful build.
12pub async fn fetch_rustdoc_json(
13    name: &str,
14    version: &str,
15    client: &ClientWithMiddleware,
16    cache: &DiskCache,
17) -> Result<RustdocJson> {
18    let url = format!("{DOCSRS_BASE}/crate/{name}/{version}/json");
19
20    // HEAD check first to avoid downloading a large file that 404s
21    let exists = cache.head_check(client, &url).await?;
22    if !exists {
23        return Err(DocsError::DocsNotFound {
24            name: name.to_string(),
25            version: version.to_string(),
26        });
27    }
28
29    let doc: RustdocJson = cache.get_zstd_json(client, &url).await?;
30
31    if doc.format_version < 33 {
32        return Err(DocsError::Other(format!(
33            "Unsupported rustdoc JSON format version: {}. Expected >= 33.",
34            doc.format_version
35        )));
36    }
37
38    Ok(doc)
39}
40
41/// Check if a docs.rs build exists for a crate version (HEAD request only).
42pub async fn docs_exist(
43    name: &str,
44    version: &str,
45    client: &ClientWithMiddleware,
46    cache: &DiskCache,
47) -> Result<bool> {
48    let url = format!("{DOCSRS_BASE}/crate/{name}/{version}/json");
49    cache.head_check(client, &url).await
50}