Skip to content

Commit

Permalink
ImageName::as_path (#129)
Browse files Browse the repository at this point in the history
Translate `ImageName` as a `PathBuf`, and its inverse procedure
  • Loading branch information
termoshtt committed May 10, 2024
1 parent a85c042 commit 50a21b8
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 48 deletions.
87 changes: 84 additions & 3 deletions ocipkg/src/image_name.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::distribution::{Name, Reference};
use anyhow::Result;
use std::fmt;
use anyhow::{anyhow, bail, Context, Result};
use std::{
fmt,
path::{Path, PathBuf},
};
use url::Url;

/// Image name
Expand Down Expand Up @@ -169,11 +172,68 @@ impl ImageName {
};
Ok(Url::parse(&url)?)
}

/// Encode image name into a path by `{hostname}/{name}/__{reference}` or `{hostname}__{port}/{name}/__{reference}` if port is specified.
pub fn as_path(&self) -> PathBuf {
PathBuf::from(if let Some(port) = self.port {
format!(
"{}__{}/{}/__{}",
self.hostname, port, self.name, self.reference
)
} else {
format!("{}/{}/__{}", self.hostname, self.name, self.reference)
})
}

/// Parse image name from a path encoded by [ImageName::as_path]
pub fn from_path(path: &Path) -> Result<Self> {
let components = path
.components()
.map(|c| {
c.as_os_str()
.to_str()
.context("Try to convert a path including non UTF-8 character")
})
.collect::<Result<Vec<&str>>>()?;
let n = components.len();
if n < 3 {
bail!(
"Path for image name must consist of registry, name, and tag: {}",
path.display()
);
}

let registry = &components[0];
let mut iter = registry.split("__");
let hostname = iter
.next()
.with_context(|| anyhow!("Invalid registry: {registry}"))?
.to_string();
let port = iter
.next()
.map(|port| str::parse(port).context("Invalid port number"))
.transpose()?;

let name = Name::new(&components[1..n - 1].join("/"))?;

let reference = Reference::new(
components[n - 1]
.strip_prefix("__")
.with_context(|| anyhow!("Missing tag in path: {}", path.display()))?,
)?;

Ok(ImageName {
hostname,
port,
name,
reference,
})
}
}

#[cfg(test)]
mod test {
use crate::ImageName;
use super::*;

#[test]
fn ttlsh_style() {
Expand All @@ -187,4 +247,25 @@ mod test {
);
assert_eq!(image_name.reference.as_str(), "1h")
}

fn test_as_path(name: &str, path: &Path) -> Result<()> {
let image_name = ImageName::parse(name)?;
assert_eq!(image_name.as_path(), path);
assert_eq!(ImageName::from_path(&image_name.as_path())?, image_name);
Ok(())
}

#[test]
fn as_path() -> Result<()> {
test_as_path(
"localhost:5000/test_repo:latest",
"localhost__5000/test_repo/__latest".as_ref(),
)?;
test_as_path(
"ubuntu:20.04",
"registry-1.docker.io/ubuntu/__20.04".as_ref(),
)?;
test_as_path("alpine", "registry-1.docker.io/alpine/__latest".as_ref())?;
Ok(())
}
}
48 changes: 3 additions & 45 deletions ocipkg/src/local/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
//! Manage container images stored in local storage

use crate::{
distribution::{Name, Reference},
ImageName,
};
use crate::ImageName;
use anyhow::{anyhow, Result};
use directories::ProjectDirs;
use std::{path::*, sync::OnceLock};
Expand Down Expand Up @@ -31,53 +28,14 @@ pub fn data_dir() -> Result<PathBuf> {

/// Resolve a path to local storage where the image will be stored
pub fn image_dir(name: &ImageName) -> Result<PathBuf> {
let dir = data_dir()?;
if let Some(port) = name.port {
Ok(dir.join(format!(
"{}__{}/{}/__{}",
name.hostname, port, name.name, name.reference
)))
} else {
Ok(dir.join(format!(
"{}/{}/__{}",
name.hostname, name.name, name.reference
)))
}
Ok(data_dir()?.join(name.as_path()))
}

fn path_to_image_name(path: &Path) -> Result<ImageName> {
let rel_path = path
.strip_prefix(data_dir()?)
.expect("WalkDir must return path under data_dir");
let components: Vec<_> = rel_path
.components()
.map(|c| {
c.as_os_str()
.to_str()
.expect("Non UTF-8 charactor never included here")
})
.collect();
let n = components.len();
assert!(n >= 3);
let registry = &components[0];
let name = Name::new(&components[1..n - 1].join("/"))?;
let reference = Reference::new(
components[n - 1]
.strip_prefix("__")
.expect("This has been checked"),
)?;

let mut iter = registry.split("__");
let hostname = iter.next().unwrap().to_string();
let port = iter
.next()
.map(|port| str::parse(port).expect("Invalid port number"));
Ok(ImageName {
hostname,
port,
name,
reference,
})
ImageName::from_path(rel_path)
}

/// Get images stored in local storage
Expand Down

0 comments on commit 50a21b8

Please sign in to comment.