docker_registry_client/image/
image_name.rs

1use either::Either;
2
3pub mod digest;
4pub mod tag;
5
6use digest::Digest;
7use tag::Tag;
8
9#[derive(Debug)]
10pub enum FromStrError {
11    MissingNameDigest,
12    MissingNameTag,
13    MissingDigest,
14    ParseDigest(digest::FromStrError),
15    ParseTag(tag::FromStrError),
16}
17
18#[derive(Debug, PartialEq, Clone, Eq, Hash)]
19pub struct ImageName {
20    pub name: String,
21    pub identifier: Either<Tag, Digest>,
22}
23
24impl std::fmt::Display for FromStrError {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self {
27            Self::MissingNameDigest => f.write_str("missing name and digest"),
28            Self::MissingNameTag => f.write_str("missing name and tag"),
29            Self::MissingDigest => f.write_str("missing digest"),
30            Self::ParseDigest(e) => write!(f, "error parsing digest: {e}"),
31            Self::ParseTag(e) => write!(f, "error parsing tag: {e}"),
32        }
33    }
34}
35
36impl std::error::Error for FromStrError {}
37
38impl std::fmt::Display for ImageName {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        write!(
41            f,
42            "{image_name}:{identifier}",
43            image_name = self.name,
44            identifier = match &self.identifier {
45                Either::Left(tag) => tag.to_string(),
46                Either::Right(digest) => digest.to_string(),
47            }
48        )
49    }
50}
51
52impl std::str::FromStr for ImageName {
53    type Err = FromStrError;
54
55    fn from_str(s: &str) -> Result<Self, Self::Err> {
56        let has_digest = s.contains('@');
57
58        if has_digest {
59            let parts: Vec<&str> = s.split('@').collect();
60            let name = (*parts.first().ok_or(Self::Err::MissingNameDigest)?).to_string();
61
62            let digest = parts
63                .get(1)
64                .ok_or(Self::Err::MissingDigest)?
65                .parse()
66                .map_err(Self::Err::ParseDigest)?;
67
68            Ok(Self {
69                name,
70                identifier: Either::Right(digest),
71            })
72        } else {
73            let parts: Vec<&str> = s.split(':').collect();
74            let name = (*parts.first().ok_or(Self::Err::MissingNameTag)?).to_string();
75
76            let tag = parts
77                .get(1)
78                .unwrap_or(&"latest")
79                .parse()
80                .map_err(Self::Err::ParseTag)?;
81
82            Ok(Self {
83                name,
84                identifier: Either::Left(tag),
85            })
86        }
87    }
88}