diff --git a/src/utils/reference.rs b/src/utils/reference.rs index 346ccab..8d544b4 100644 --- a/src/utils/reference.rs +++ b/src/utils/reference.rs @@ -1,51 +1,48 @@ -use once_cell::sync::Lazy; -use regex::Regex; - -use crate::error; - const DEFAULT_REGISTRY: &str = "registry-1.docker.io"; /// Takes an image and splits it into registry, repository and tag, based on the reference. /// For example, `ghcr.io/sergi0g/cup:latest` becomes `['ghcr.io', 'sergi0g/cup', 'latest']`. pub fn split(reference: &str) -> (String, String, String) { - match REFERENCE_REGEX.captures(reference) { - Some(c) => { - let registry = match c.name("registry") { - Some(registry) => registry.as_str().to_owned(), - None => String::from(DEFAULT_REGISTRY), - }; - let is_default_registry = registry == DEFAULT_REGISTRY; - return ( - registry, - match c.name("repository") { - Some(repository) => { - let repo = repository.as_str().to_owned(); - if !repo.contains('/') && is_default_registry { - format!("library/{}", repo) - } else { - repo - } - } - None => error!("Failed to parse image {}", reference), - }, - match c.name("tag") { - Some(tag) => tag.as_str().to_owned(), - None => String::from("latest"), - }, - ); + let splits = reference.split('/').collect::>(); + let (registry, repository_and_tag) = match splits.len() { + 0 => unreachable!(), + 1 => (DEFAULT_REGISTRY, reference.to_string()), + _ => { + // Check if we're looking at a domain + if splits[0] == "localhost" || splits[0].contains('.') || splits[0].contains(':') { + (splits[0], splits[1..].join("/")) + } else { + (DEFAULT_REGISTRY, reference.to_string()) + } } - None => error!("Failed to parse image {}", reference), - } + }; + let splits = repository_and_tag.split(':').collect::>(); + let (repository, tag) = match splits.len() { + 1 | 2 => { + let repository_components = splits[0].split('/').collect::>(); + let repository = match repository_components.len() { + 0 => unreachable!(), + 1 => { + if registry == DEFAULT_REGISTRY { + format!("library/{}", repository_components[0]) + } else { + splits[0].to_string() + } + } + _ => splits[0].to_string(), + }; + let tag = match splits.len() { + 1 => "latest", + 2 => splits[1], + _ => unreachable!(), + }; + (repository, tag) + } + _ => unreachable!(), + }; + (registry.to_string(), repository, tag.to_string()) } -/// Regex to match Docker image references against, so registry, repository and tag can be extracted. -static REFERENCE_REGEX: Lazy = Lazy::new(|| { - Regex::new( - r#"^(?P(?:(?P(?:(?:localhost|[\w-]+(?:\.[\w-]+)+)(?::\d+)?)|[\w]+:\d+)/)?(?P[a-z0-9_.-]+(?:/[a-z0-9_.-]+)*))(?::(?P[\w][\w.-]{0,127}))?$"#, // From https://regex101.com/r/nmSDPA/1 - ) - .unwrap() -}); - #[cfg(test)] mod tests { use super::*; @@ -53,17 +50,17 @@ mod tests { #[test] #[rustfmt::skip] fn reference() { - assert_eq!(split("alpine"), (String::from(DEFAULT_REGISTRY), String::from("library/alpine"), String::from("latest"))); - assert_eq!(split("alpine:latest"), (String::from(DEFAULT_REGISTRY), String::from("library/alpine"), String::from("latest"))); - assert_eq!(split("library/alpine"), (String::from(DEFAULT_REGISTRY), String::from("library/alpine"), String::from("latest"))); - assert_eq!(split("localhost/test"), (String::from("localhost"), String::from("test"), String::from("latest"))); - assert_eq!(split("localhost:1234/test"), (String::from("localhost:1234"), String::from("test"), String::from("latest"))); - assert_eq!(split("test:1234/idk"), (String::from("test:1234"), String::from("idk"), String::from("latest"))); - assert_eq!(split("alpine:3.7"), (String::from(DEFAULT_REGISTRY), String::from("library/alpine"), String::from("3.7"))); - assert_eq!(split("docker.example.com/examplerepo/alpine:3.7"), (String::from("docker.example.com"), String::from("examplerepo/alpine"), String::from("3.7"))); - assert_eq!(split("docker.example.com/examplerepo/alpine/test2:3.7"), (String::from("docker.example.com"), String::from("examplerepo/alpine/test2"), String::from("3.7"))); - assert_eq!(split("docker.example.com/examplerepo/alpine/test2/test3:3.7"), (String::from("docker.example.com"), String::from("examplerepo/alpine/test2/test3"), String::from("3.7"))); - assert_eq!(split("docker.example.com:5000/examplerepo/alpine:latest"), (String::from("docker.example.com:5000"), String::from("examplerepo/alpine"), String::from("latest"))); - assert_eq!(split("portainer/portainer:latest"), (String::from(DEFAULT_REGISTRY), String::from("portainer/portainer"), String::from("latest"))); + assert_eq!(split("alpine" ), (String::from(DEFAULT_REGISTRY ), String::from("library/alpine" ), String::from("latest"))); + assert_eq!(split("alpine:latest" ), (String::from(DEFAULT_REGISTRY ), String::from("library/alpine" ), String::from("latest"))); + assert_eq!(split("library/alpine" ), (String::from(DEFAULT_REGISTRY ), String::from("library/alpine" ), String::from("latest"))); + assert_eq!(split("localhost/test" ), (String::from("localhost" ), String::from("test" ), String::from("latest"))); + assert_eq!(split("localhost:1234/test" ), (String::from("localhost:1234" ), String::from("test" ), String::from("latest"))); + assert_eq!(split("test:1234/idk" ), (String::from("test:1234" ), String::from("idk" ), String::from("latest"))); + assert_eq!(split("alpine:3.7" ), (String::from(DEFAULT_REGISTRY ), String::from("library/alpine" ), String::from("3.7" ))); + assert_eq!(split("docker.example.com/examplerepo/alpine:3.7" ), (String::from("docker.example.com" ), String::from("examplerepo/alpine" ), String::from("3.7" ))); + assert_eq!(split("docker.example.com/examplerepo/alpine/test2:3.7" ), (String::from("docker.example.com" ), String::from("examplerepo/alpine/test2" ), String::from("3.7" ))); + assert_eq!(split("docker.example.com/examplerepo/alpine/test2/test3:3.7"), (String::from("docker.example.com" ), String::from("examplerepo/alpine/test2/test3"), String::from("3.7" ))); + assert_eq!(split("docker.example.com:5000/examplerepo/alpine:latest" ), (String::from("docker.example.com:5000"), String::from("examplerepo/alpine" ), String::from("latest"))); + assert_eq!(split("portainer/portainer:latest" ), (String::from(DEFAULT_REGISTRY ), String::from("portainer/portainer" ), String::from("latest"))); } }