Skip to main content

vyre_runtime/pipeline_cache/
remote.rs

1//! HTTPS-backed read-through cache. Feature-gated on `remote-cache` so library
2//! users who only want disk caching don't pull in `ureq`.
3
4use super::disk::read_verified_cache_blob;
5use super::fingerprint::PipelineFingerprint;
6use super::store::PipelineCacheStore;
7
8/// HTTPS-backed cache that reads pre-compiled artifacts from a
9/// base URL. Feature-gated on `remote-cache` so library users who only
10/// want disk caching don't pull in `ureq`.
11///
12/// Writes are **no-ops**  -  `RemoteCache` is a read-through layer.
13/// Publishing to a remote registry is a separate `vyre publish-cache`
14/// xtask, not part of this runtime.
15pub struct RemoteCache {
16    base_url: String,
17    agent: ureq::Agent,
18}
19
20impl RemoteCache {
21    /// Construct from a base URL. The cache fetches
22    /// `<base_url>/<fp_hex>.bin` for each lookup.
23    #[must_use]
24    pub fn new(base_url: impl Into<String>) -> Self {
25        Self {
26            base_url: base_url.into(),
27            agent: ureq::Agent::new_with_defaults(),
28        }
29    }
30}
31
32impl PipelineCacheStore for RemoteCache {
33    fn get(&self, fp: &PipelineFingerprint) -> Option<Vec<u8>> {
34        let url = format!("{}/{}.bin", self.base_url.trim_end_matches('/'), fp.hex());
35        let mut resp = self.agent.get(&url).call().ok()?;
36        read_verified_cache_blob(resp.body_mut().as_reader())
37    }
38
39    fn put(&self, _fp: PipelineFingerprint, _artifact: Vec<u8>) {
40        // Remote cache is read-through; publishing is a separate flow.
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::RemoteCache;
47
48    #[test]
49    fn remote_cache_owns_reusable_http_agent() {
50        let cache = RemoteCache::new("https://cache.invalid/root/");
51
52        assert_eq!(cache.base_url, "https://cache.invalid/root/");
53        let _shared_agent = cache.agent.clone();
54    }
55}