use std::fs;
use std::path::Path;
use super::{Image, STDIN_IMAGE};
use crate::{Error, Result};
impl Image {
pub fn resolve(image: &str) -> Result<Self> {
if image == STDIN_IMAGE {
return Err(Error::invalid_image(
image,
"an image on stdin is not supported; pass the archive's path",
));
}
if Self::is_local(image) {
let path =
fs::canonicalize(image).map_err(|source| Error::io(Path::new(image), source))?;
let path = path
.into_os_string()
.into_string()
.ok()
.filter(|path| Path::new(path).is_file())
.ok_or_else(|| {
Error::invalid_image(image, "a local image must be a docker save archive file")
})?;
return Self::parse(&path);
}
Image::parse(image)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resolves_an_archive_path_to_its_canonical_form() {
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("guest.tar");
fs::write(&path, "").unwrap();
let image = Image::resolve(path.to_str().unwrap()).unwrap();
let canonical = path.canonicalize().unwrap();
assert_eq!(image.as_str(), canonical.to_str().unwrap());
}
#[test]
fn a_missing_archive_is_an_io_error() {
let error = Image::resolve("./guest.tar").unwrap_err();
assert_eq!(error.to_string(), "failed to access ./guest.tar");
assert!(matches!(error, Error::Io { .. }));
}
#[test]
fn refuses_stdin_and_directories() {
let tempdir = tempfile::tempdir().unwrap();
let dir = tempdir.path().to_str().unwrap();
assert_eq!(
Image::resolve("-").unwrap_err().to_string(),
"invalid image \"-\": an image on stdin is not supported; pass the archive's path"
);
assert_eq!(
Image::resolve(dir).unwrap_err().to_string(),
format!("invalid image \"{dir}\": a local image must be a docker save archive file")
);
}
#[test]
fn a_registry_reference_stays_as_given() {
let pinned = format!("ghcr.io/org/name@sha256:{}", "a".repeat(64));
for reference in ["ghcr.io/org/name:latest", pinned.as_str()] {
assert_eq!(Image::resolve(reference).unwrap().as_str(), reference);
}
}
}