use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImageRef {
pub registry: Option<String>,
pub image: String,
pub tag: Option<String>,
pub hash: Option<String>
}
fn is_registry(token: &str) -> bool {
token == "localhost" || token.contains('.') || token.contains(':')
}
impl ImageRef {
pub fn parse(s: &str) -> ImageRef {
let parts: Vec<&str> = s.splitn(2, '/').collect();
let (registry, image_full) = if parts.len() == 2 && is_registry(parts[0]) {
(Some(parts[0].to_string()), parts[1])
} else {
(None, s)
};
if let Some(at_pos) = image_full.find('@') {
let (image, hash) = image_full.split_at(at_pos);
ImageRef {
registry,
image: image.to_string(),
hash: Some(hash[1..].to_string()),
tag: None
}
} else {
let parts: Vec<&str> = image_full.splitn(2, ':').collect();
let image = parts[0].to_string();
let tag = parts.get(1).map(|p| String::from(*p));
ImageRef { registry, image, tag, hash: None }
}
}
}
impl fmt::Display for ImageRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(registry) = &self.registry {
write!(f, "{}/", registry)?;
}
write!(f, "{}", self.image)?;
if let Some(tag) = &self.tag {
write!(f, ":{}", tag)?;
} else if let Some(hash) = &self.hash {
write!(f, "@{}", hash)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_image_parse_dockerhub() {
assert_eq!(
ImageRef::parse("alpine:3.10"),
ImageRef {
registry: None,
image: "alpine".into(),
tag: Some("3.10".into()),
hash: None
}
);
assert_eq!(
ImageRef::parse("foo/bar"),
ImageRef {
registry: None,
image: "foo/bar".into(),
tag: None,
hash: None
}
);
assert_eq!(
ImageRef::parse("clux/muslrust"),
ImageRef {
registry: None,
image: "clux/muslrust".into(),
tag: None,
hash: None
}
);
assert_eq!(
ImageRef::parse("clux/muslrust:1.41.0-stable"),
ImageRef {
registry: None,
image: "clux/muslrust".into(),
tag: Some("1.41.0-stable".into()),
hash: None
}
);
assert_eq!(
ImageRef::parse("fake_project/fake_image@fake_hash"),
ImageRef {
registry: None,
image: "fake_project/fake_image".into(),
tag: None,
hash: Some("fake_hash".into())
}
);
assert_eq!(
ImageRef::parse("fake_project/fake_image@"),
ImageRef {
registry: None,
image: "fake_project/fake_image".into(),
tag: None,
hash: Some("".into())
}
);
assert_eq!(
ImageRef::parse("fake_project/fake_image@sha256:"),
ImageRef {
registry: None,
image: "fake_project/fake_image".into(),
tag: None,
hash: Some("sha256:".into())
}
);
}
#[test]
fn test_image_parse_registry() {
assert_eq!(
ImageRef::parse("quay.io/prometheus/node-exporter:v0.18.1"),
ImageRef {
registry: Some("quay.io".into()),
image: "prometheus/node-exporter".into(),
tag: Some("v0.18.1".into()),
hash: None
}
);
assert_eq!(
ImageRef::parse("gcr.io/fake_project/fake_image:fake_tag"),
ImageRef {
registry: Some("gcr.io".into()),
image: "fake_project/fake_image".into(),
tag: Some("fake_tag".into()),
hash: None
}
);
assert_eq!(
ImageRef::parse("gcr.io/fake_project/fake_image"),
ImageRef {
registry: Some("gcr.io".into()),
image: "fake_project/fake_image".into(),
tag: None,
hash: None
}
);
assert_eq!(
ImageRef::parse("gcr.io/fake_image"),
ImageRef {
registry: Some("gcr.io".into()),
image: "fake_image".into(),
tag: None,
hash: None
}
);
assert_eq!(
ImageRef::parse("gcr.io/fake_image:fake_tag"),
ImageRef {
registry: Some("gcr.io".into()),
image: "fake_image".into(),
tag: Some("fake_tag".into()),
hash: None
}
);
assert_eq!(
ImageRef::parse("quay.io/fake_project/fake_image@fake_hash"),
ImageRef {
registry: Some("quay.io".into()),
image: "fake_project/fake_image".into(),
tag: None,
hash: Some("fake_hash".into())
}
);
}
#[test]
fn test_image_parse_localhost() {
assert_eq!(
ImageRef::parse("localhost/foo"),
ImageRef {
registry: Some("localhost".into()),
image: "foo".into(),
tag: None,
hash: None
}
);
assert_eq!(
ImageRef::parse("localhost/foo:bar"),
ImageRef {
registry: Some("localhost".into()),
image: "foo".into(),
tag: Some("bar".into()),
hash: None
}
);
assert_eq!(
ImageRef::parse("localhost/foo/bar"),
ImageRef {
registry: Some("localhost".into()),
image: "foo/bar".into(),
tag: None,
hash: None
}
);
assert_eq!(
ImageRef::parse("localhost/foo/bar:baz"),
ImageRef {
registry: Some("localhost".into()),
image: "foo/bar".into(),
tag: Some("baz".into()),
hash: None
}
);
}
#[test]
fn test_image_parse_registry_port() {
assert_eq!(
ImageRef::parse("example.com:1234/foo"),
ImageRef {
registry: Some("example.com:1234".into()),
image: "foo".into(),
tag: None,
hash: None
}
);
assert_eq!(
ImageRef::parse("example.com:1234/foo:bar"),
ImageRef {
registry: Some("example.com:1234".into()),
image: "foo".into(),
tag: Some("bar".into()),
hash: None
}
);
assert_eq!(
ImageRef::parse("example.com:1234/foo/bar"),
ImageRef {
registry: Some("example.com:1234".into()),
image: "foo/bar".into(),
tag: None,
hash: None
}
);
assert_eq!(
ImageRef::parse("example.com:1234/foo/bar:baz"),
ImageRef {
registry: Some("example.com:1234".into()),
image: "foo/bar".into(),
tag: Some("baz".into()),
hash: None
}
);
assert_eq!(
ImageRef::parse("example.com:1234/foo/bar/baz:qux"),
ImageRef {
registry: Some("example.com:1234".into()),
image: "foo/bar/baz".into(),
tag: Some("qux".into()),
hash: None
}
);
}
}