use crate::source::{LayerRef, LayerSource, SourceError};
use std::cell::RefCell;
pub struct Mirror {
pub label: String,
pub source: Box<dyn LayerSource>,
}
pub struct Mirrors {
mirrors: Vec<Mirror>,
served_by: RefCell<Option<String>>,
attempts: RefCell<Vec<(String, String)>>,
}
impl Mirrors {
pub fn new(mirrors: Vec<Mirror>) -> Self {
Mirrors {
mirrors,
served_by: RefCell::new(None),
attempts: RefCell::new(Vec::new()),
}
}
pub fn served_by(&self) -> Option<String> {
self.served_by.borrow().clone()
}
pub fn attempts(&self) -> Vec<(String, String)> {
self.attempts.borrow().clone()
}
pub fn labels(&self) -> Vec<&str> {
self.mirrors.iter().map(|m| m.label.as_str()).collect()
}
fn try_each<T>(
&self,
what: &str,
mut f: impl FnMut(&dyn LayerSource) -> Result<T, SourceError>,
) -> Result<T, SourceError> {
self.attempts.borrow_mut().clear();
if self.mirrors.is_empty() {
return Err(SourceError::Transport(
"this realm declares no sources at all".into(),
));
}
for m in &self.mirrors {
match f(m.source.as_ref()) {
Ok(v) => {
*self.served_by.borrow_mut() = Some(m.label.clone());
return Ok(v);
}
Err(e) => {
self.attempts
.borrow_mut()
.push((m.label.clone(), e.to_string()));
}
}
}
let tried = self
.attempts
.borrow()
.iter()
.map(|(l, e)| format!("\n {l}: {e}"))
.collect::<String>();
Err(SourceError::Transport(format!(
"no source could supply {what}. Tried {} source(s):{tried}",
self.mirrors.len()
)))
}
}
impl LayerSource for Mirrors {
fn fetch_manifest(&self, layer: &LayerRef) -> Result<Vec<u8>, SourceError> {
self.try_each("the layer manifest", |s| s.fetch_manifest(layer))
}
fn fetch_blob(&self, digest: &str) -> Result<Vec<u8>, SourceError> {
self.try_each(&format!("blob {digest}"), |s| s.fetch_blob(digest))
}
fn fetch_line_status(&self, layer: &LayerRef) -> Result<Option<Vec<u8>>, SourceError> {
self.try_each_optional("the line-status document", |s| s.fetch_line_status(layer))
}
fn fetch_line_index(&self, line: &str) -> Result<Option<Vec<u8>>, SourceError> {
self.try_each_optional("the line index", |s| s.fetch_line_index(line))
}
fn fetch_attestations(
&self,
layer: &LayerRef,
) -> Result<Vec<crate::attestcarry::CarriedAttestation>, SourceError> {
self.try_each("the attestations", |s| s.fetch_attestations(layer))
}
}
impl Mirrors {
fn try_each_optional(
&self,
what: &str,
mut f: impl FnMut(&dyn LayerSource) -> Result<Option<Vec<u8>>, SourceError>,
) -> Result<Option<Vec<u8>>, SourceError> {
self.attempts.borrow_mut().clear();
let mut any_answered = false;
for m in &self.mirrors {
match f(m.source.as_ref()) {
Ok(Some(v)) => {
*self.served_by.borrow_mut() = Some(m.label.clone());
return Ok(Some(v));
}
Ok(None) => {
any_answered = true;
self.attempts
.borrow_mut()
.push((m.label.clone(), "carries none".into()));
}
Err(e) => {
self.attempts
.borrow_mut()
.push((m.label.clone(), e.to_string()));
}
}
}
if any_answered {
return Ok(None);
}
let tried = self
.attempts
.borrow()
.iter()
.map(|(l, e)| format!("\n {l}: {e}"))
.collect::<String>();
Err(SourceError::Transport(format!(
"no source could be reached for {what}:{tried}"
)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
use std::rc::Rc;
struct Fake {
manifest: Option<Vec<u8>>,
err: Option<SourceError>,
status: Option<Option<Vec<u8>>>,
calls: Rc<Cell<usize>>,
}
fn ok(bytes: &[u8]) -> Box<Fake> {
Box::new(Fake {
manifest: Some(bytes.to_vec()),
err: None,
status: Some(Some(bytes.to_vec())),
calls: Rc::new(Cell::new(0)),
})
}
fn down(msg: &str) -> Box<Fake> {
Box::new(Fake {
manifest: None,
err: Some(SourceError::Transport(msg.into())),
status: None,
calls: Rc::new(Cell::new(0)),
})
}
fn empty() -> Box<Fake> {
Box::new(Fake {
manifest: None,
err: Some(SourceError::NotFound("layer".into())),
status: Some(None),
calls: Rc::new(Cell::new(0)),
})
}
impl LayerSource for Fake {
fn fetch_manifest(&self, _l: &LayerRef) -> Result<Vec<u8>, SourceError> {
self.calls.set(self.calls.get() + 1);
match (&self.manifest, &self.err) {
(Some(m), _) => Ok(m.clone()),
(None, Some(e)) => Err(clone_err(e)),
_ => Err(SourceError::NotFound("x".into())),
}
}
fn fetch_blob(&self, _d: &str) -> Result<Vec<u8>, SourceError> {
self.fetch_manifest(&LayerRef::Digest("x".into()))
}
fn fetch_line_status(&self, _l: &LayerRef) -> Result<Option<Vec<u8>>, SourceError> {
self.calls.set(self.calls.get() + 1);
match (&self.status, &self.err) {
(Some(s), _) => Ok(s.clone()),
(None, Some(e)) => Err(clone_err(e)),
_ => Ok(None),
}
}
fn fetch_line_index(&self, _line: &str) -> Result<Option<Vec<u8>>, SourceError> {
self.fetch_line_status(&LayerRef::Digest("x".into()))
}
fn fetch_attestations(
&self,
_l: &LayerRef,
) -> Result<Vec<crate::attestcarry::CarriedAttestation>, SourceError> {
self.calls.set(self.calls.get() + 1);
match (&self.manifest, &self.err) {
(Some(_), _) => Ok(Vec::new()),
(None, Some(e)) => Err(clone_err(e)),
_ => Ok(Vec::new()),
}
}
}
fn clone_err(e: &SourceError) -> SourceError {
match e {
SourceError::NotFound(s) => SourceError::NotFound(s.clone()),
SourceError::Transport(s) => SourceError::Transport(s.clone()),
other => SourceError::Transport(other.to_string()),
}
}
fn mirrors(v: Vec<(&str, Box<Fake>)>) -> Mirrors {
Mirrors::new(
v.into_iter()
.map(|(l, s)| Mirror {
label: l.into(),
source: s as Box<dyn LayerSource>,
})
.collect(),
)
}
#[test]
fn the_first_source_that_answers_serves_the_layer() {
let m = mirrors(vec![("primary", ok(b"manifest")), ("mirror", ok(b"other"))]);
assert_eq!(
m.fetch_manifest(&LayerRef::Digest("d".into())).unwrap(),
b"manifest".to_vec()
);
assert_eq!(m.served_by().as_deref(), Some("primary"));
}
#[test]
fn an_unreachable_source_falls_through_to_the_next() {
let m = mirrors(vec![
("primary", down("dial tcp: no such host")),
("mirror", ok(b"manifest")),
]);
assert_eq!(
m.fetch_manifest(&LayerRef::Digest("d".into())).unwrap(),
b"manifest".to_vec()
);
assert_eq!(m.served_by().as_deref(), Some("mirror"));
}
#[test]
fn a_source_that_lacks_the_layer_falls_through_too() {
let m = mirrors(vec![("primary", empty()), ("mirror", ok(b"manifest"))]);
assert!(m.fetch_manifest(&LayerRef::Digest("d".into())).is_ok());
assert_eq!(m.served_by().as_deref(), Some("mirror"));
}
#[test]
fn which_source_served_the_layer_is_reportable() {
let m = mirrors(vec![
("oci://primary", down("503")),
("oci://backup", ok(b"m")),
]);
m.fetch_manifest(&LayerRef::Digest("d".into())).unwrap();
assert_eq!(m.served_by().as_deref(), Some("oci://backup"));
let attempts = m.attempts();
assert_eq!(attempts.len(), 1);
assert_eq!(attempts[0].0, "oci://primary");
assert!(attempts[0].1.contains("503"), "{attempts:?}");
}
#[test]
fn when_no_source_answers_the_error_names_all_of_them() {
let m = mirrors(vec![
("oci://a", down("no such host")),
("oci://b", down("503 Service Unavailable")),
]);
let e = m
.fetch_manifest(&LayerRef::Digest("d".into()))
.expect_err("must fail");
let msg = e.to_string();
assert!(
msg.contains("oci://a") && msg.contains("no such host"),
"{msg}"
);
assert!(msg.contains("oci://b") && msg.contains("503"), "{msg}");
assert!(msg.contains("2 source(s)"), "{msg}");
assert!(m.served_by().is_none());
}
#[test]
fn a_realm_with_no_sources_says_so() {
let m = Mirrors::new(Vec::new());
let e = m
.fetch_manifest(&LayerRef::Digest("d".into()))
.expect_err("must fail");
assert!(e.to_string().contains("no sources at all"), "{e}");
}
#[test]
fn a_source_after_the_one_that_answered_is_not_consulted() {
let second = ok(b"second");
let counter = Rc::clone(&second.calls);
let m = mirrors(vec![("primary", ok(b"first")), ("mirror", second)]);
m.fetch_manifest(&LayerRef::Digest("d".into())).unwrap();
assert_eq!(counter.get(), 0, "the later source was consulted anyway");
}
#[test]
fn every_kind_of_fetch_falls_through_not_only_the_manifest() {
let m = mirrors(vec![("primary", down("503")), ("mirror", ok(b"bytes"))]);
assert_eq!(m.fetch_blob("sha256:x").unwrap(), b"bytes".to_vec());
assert_eq!(m.served_by().as_deref(), Some("mirror"));
let m = mirrors(vec![("primary", down("503")), ("mirror", ok(b"index"))]);
assert_eq!(
m.fetch_line_index("2026.09").unwrap(),
Some(b"index".to_vec())
);
assert_eq!(m.served_by().as_deref(), Some("mirror"));
let m = mirrors(vec![("primary", down("503")), ("mirror", ok(b"att"))]);
assert!(m.fetch_attestations(&LayerRef::Digest("d".into())).is_ok());
assert_eq!(m.served_by().as_deref(), Some("mirror"));
}
#[test]
fn every_kind_of_fetch_refuses_when_no_source_answers() {
let m = mirrors(vec![("a", down("no such host")), ("b", down("503"))]);
assert!(m.fetch_manifest(&LayerRef::Digest("d".into())).is_err());
assert!(m.fetch_blob("sha256:x").is_err());
assert!(m.fetch_line_index("2026.09").is_err());
assert!(m.fetch_attestations(&LayerRef::Digest("d".into())).is_err());
assert!(
m.served_by().is_none(),
"nothing served, yet a source is named"
);
}
#[test]
fn the_configured_sources_are_reportable_in_order() {
let m = mirrors(vec![("oci://a", ok(b"x")), ("oci://b", ok(b"y"))]);
assert_eq!(m.labels(), vec!["oci://a", "oci://b"]);
assert_eq!(Mirrors::new(Vec::new()).labels(), Vec::<&str>::new());
}
#[test]
fn a_document_no_source_carries_is_absent_not_unreachable() {
let m = mirrors(vec![("a", empty()), ("b", empty())]);
assert_eq!(
m.fetch_line_status(&LayerRef::Digest("d".into())).unwrap(),
None
);
}
#[test]
fn a_document_nobody_could_be_asked_about_is_not_reported_as_absent() {
let m = mirrors(vec![("a", down("no such host")), ("b", down("503"))]);
let e = m
.fetch_line_status(&LayerRef::Digest("d".into()))
.expect_err("must not report absence");
assert!(e.to_string().contains("could be reached"), "{e}");
}
#[test]
fn a_source_carrying_the_document_is_reached_past_one_that_does_not() {
let m = mirrors(vec![("a", empty()), ("b", ok(b"status"))]);
assert_eq!(
m.fetch_line_status(&LayerRef::Digest("d".into())).unwrap(),
Some(b"status".to_vec())
);
assert_eq!(m.served_by().as_deref(), Some("b"));
}
}