Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

mannequin/src/nexus/prod.rs

use super::NexusAsset;
use std::collections::HashMap;
use std::sync::Mutex;

/// Prod repo: pre-loaded with a few well-known crates. Read-only.
pub struct ProdRepo {
    assets: Mutex<HashMap<String, Vec<u8>>>,
    base_url: String,
    http_client: reqwest::Client,
}

struct SeedCrate {
    name: &'static str,
    version: &'static str,
}

const SEED_CRATES: &[SeedCrate] = &[
    SeedCrate {
        name: "serde",
        version: "1.0.200",
    },
    SeedCrate {
        name: "anyhow",
        version: "1.0.86",
    },
    SeedCrate {
        name: "log",
        version: "0.4.21",
    },
    SeedCrate {
        name: "rand",
        version: "0.8.5",
    },
];

impl ProdRepo {
    pub fn new(base_url: &str) -> Self {
        Self {
            assets: Mutex::new(HashMap::new()),
            base_url: base_url.to_string(),
            http_client: tls_roots(reqwest::Client::builder())
                .build()
                .expect("reqwest client"),
        }
    }

    /// Seed the 4 default crates (fetched lazily on first download).
    pub fn seed_defaults(&mut self) {
        // We just register the paths; actual data is fetched lazily.
        // Nothing to do here — download() handles lazy fetch.
    }

    pub fn seeded_paths() -> Vec<String> {
        SEED_CRATES
            .iter()
            .map(|c| format!("crates/{}/{}-{}.crate", c.name, c.name, c.version))
            .collect()
    }

    pub fn list_assets(&self) -> Vec<NexusAsset> {
        let cached: Vec<_> = self.assets.lock().unwrap().keys().cloned().collect();
        let mut all: Vec<_> = Self::seeded_paths().into_iter().collect();
        for path in cached {
            if !all.contains(&path) {
                all.push(path);
            }
        }
        all.into_iter()
            .map(|path| NexusAsset {
                download_url: format!("{}/repository/prod/{}", self.base_url, path),
                content_type: Some("application/octet-stream".into()),
                size: None,
                path,
            })
            .collect()
    }

    /// Async (M1): previously this used `block_in_place` + `Handle::block_on`,
    /// which **panics** on a current-thread runtime (in-process integration
    /// tests) and otherwise parks a worker thread for the whole fetch. Awaiting
    /// the fetch directly — like `ProxyRepo::download` — is correct on any
    /// runtime flavor.
    pub async fn download(&self, path: &str) -> Option<Vec<u8>> {
        // Check if already fetched
        if let Some(data) = self.assets.lock().unwrap().get(path) {
            return Some(data.clone());
        }

        // Check if it's a seeded path — if so, lazily fetch it.
        if Self::seeded_paths().contains(&path.to_string()) {
            let filename = path.rsplit('/').next()?;
            let stem = filename.strip_suffix(".crate")?;
            let crate_name = stem.split_once('-').map(|(n, _)| n).unwrap_or(stem);
            let url = format!(
                "https://static.crates.io/crates/{}/{}.crate",
                crate_name, stem
            );

            tracing::info!("prod: lazy-fetching {}", url);
            let resp = self.http_client.get(&url).send().await.ok()?;
            if !resp.status().is_success() {
                return None;
            }
            let data = resp.bytes().await.ok().map(|b| b.to_vec())?;

            tracing::info!("prod: cached {} ({} bytes)", path, data.len());
            self.assets
                .lock()
                .unwrap()
                .insert(path.to_string(), data.clone());
            return Some(data);
        }

        None
    }
}

/// reqwest 0.13 with reqwest 0.12 `rustls-tls` behaviour (2026-10-02): the ring
/// provider and the bundled Mozilla roots (webpki), not 0.13's aws-lc-rs + OS
/// trust store. Roots added later on the builder are merged in, as in 0.12.
#[allow(dead_code)]
fn tls_roots(b: reqwest::ClientBuilder) -> reqwest::ClientBuilder {
    let _ = rustls::crypto::ring::default_provider().install_default();
    b.tls_certs_only(
        webpki_root_certs::TLS_SERVER_ROOT_CERTS
            .iter()
            .filter_map(|c| reqwest::Certificate::from_der(c).ok()),
    )
}