rectangle_device_sandbox/
types.rs

1use regex::Regex;
2use thiserror::Error;
3
4#[derive(Error, Debug)]
5pub enum SandboxError {
6    #[error("the string `{0}` is not a valid image digest")]
7    InvalidDigest(String),
8    #[error("the string `{0}` is not a valid image name")]
9    InvalidImage(String),
10    #[error("unexpected digest `{0}`")]
11    UnexpectedDigest(String),
12}
13
14#[derive(Clone, Debug)]
15pub struct ImageDigest{
16    pub image: Image,
17    pub digest: Digest
18}
19
20impl ImageDigest {
21    pub fn parse(image: &str, digest: &str) -> Result<ImageDigest, SandboxError> {
22        Ok(ImageDigest{
23            image: Image::parse(image)?,
24            digest: Digest::parse(digest)?
25        })
26    }
27}
28
29#[derive(Clone, Debug)]
30pub struct Image {
31    inner: String
32}
33
34impl Image {
35    pub fn parse(s: &str) -> Result<Image, SandboxError> {
36        lazy_static! {
37            static ref RE: Regex = Regex::new(
38                // https://github.com/docker/distribution/blob/master/reference/regexp.go
39                r"(?x)^
40                (?:                     # Optional domain
41                    (?:                 # First domain component
42                        [a-zA-Z0-9] |
43                        [a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]
44                    )
45                    (?:                 # Optional additional domain components
46                        \.
47                        (?:
48                            [a-zA-Z0-9] |
49                            [a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]
50                        )
51                    )*
52                    (?:                 # Optional port number
53                        :[0-9]+
54                    )?
55                    /
56                )?
57                (?:                     # Main name component
58                    [a-z0-9]+
59                    (?:
60                        (?: [._] | __ | [-]* )
61                        [a-z0-9]+
62                    )*
63                )
64                (?:                     # Optional additional name components
65                    /
66                    [a-z0-9]+
67                    (?:
68                        (?: [._] | __ | [-]* )
69                        [a-z0-9]+
70                    )*
71                )*
72                (?:                     # Optional tag
73                    :
74                    [\w][\w.-]*
75                )?
76            $").unwrap();
77        }
78        let inner = s.to_owned();
79        if RE.is_match(&inner) {
80            Ok(Image { inner })
81        } else {
82            Err(SandboxError::InvalidImage(inner))
83        }
84    }
85
86    pub fn as_str(&self) -> &str {
87        self.inner.as_str()
88    }
89}
90
91#[derive(Clone, Debug)]
92pub struct Digest {
93    inner: String
94}
95
96impl Digest {
97    pub fn parse(s: &str) -> Result<Digest, SandboxError> {
98        lazy_static! {
99            static ref RE: Regex = Regex::new(
100                // https://github.com/docker/distribution/blob/master/reference/regexp.go
101                r"(?x)^
102                    (?:                 # Optional type
103                        [A-Za-z][A-Za-z0-9]*
104                        (?:
105                            [-_+.][A-Za-z][A-Za-z0-9]*
106                        )*
107                        :
108                    )?
109                    [[:xdigit:]]{32,}   # At least a 32-digit hex number
110                $").unwrap();
111        }
112        let inner = s.to_owned();
113        if RE.is_match(&inner) {
114            Ok(Digest { inner })
115        } else {
116            Err(SandboxError::InvalidDigest(inner))
117        }
118    }
119
120    pub fn as_str(&self) -> &str {
121        self.inner.as_str()
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn parse_image_name() {
131        assert!(Image::parse("balls").is_ok());
132        assert!(Image::parse("balls/").is_err());
133        assert!(Image::parse("balls/etc").is_ok());
134        assert!(Image::parse("balls/etc/and/more").is_ok());
135        assert!(Image::parse("b-a-l-l-s").is_ok());
136        assert!(Image::parse("-balls").is_err());
137        assert!(Image::parse("--balls").is_err());
138        assert!(Image::parse("b--alls").is_ok());
139        assert!(Image::parse("balls.io/image/of/my/balls").is_ok());
140        assert!(Image::parse("balls.io/image/of/my/balls:").is_err());
141        assert!(Image::parse("balls.io/image/of/my/balls:?").is_err());
142        assert!(Image::parse("balls.io/image/of/my/balls:0").is_ok());
143        assert!(Image::parse("balls.io/image/of/my/balls:.").is_err());
144        assert!(Image::parse("balls.io/image/of/my/balls:0.0").is_ok());
145        assert!(Image::parse("balls.io/image/of//balls").is_err());
146        assert!(Image::parse(" balls").is_err());
147        assert!(Image::parse("balls ").is_err());
148        assert!(Image::parse("balls:69").is_ok());
149        assert!(Image::parse("balls:6.9").is_ok());
150        assert!(Image::parse("balls:").is_err());
151        assert!(Image::parse("balls.io:69/ball").is_ok());
152        assert!(Image::parse("balls.io:/ball").is_err());
153    }
154
155    #[test]
156    fn parse_digest_name() {
157        assert!(Digest::parse("balls").is_err());
158        assert!(Digest::parse("balls:0123456789abcdef0123456789abcdef").is_ok());
159        assert!(Digest::parse("-balls:0123456789abcdef0123456789abcdef").is_err());
160        assert!(Digest::parse("--balls:0123456789abcdef0123456789abcdef").is_err());
161        assert!(Digest::parse("b_b+b+b+b+b+b.balllllls:0123456789abcdef0123456789abcdef").is_ok());
162        assert!(Digest::parse("b_b+b+b++b+b.balllllls:0123456789abcdef0123456789abcdef").is_err());
163        assert!(Digest::parse("balls:0123456789abcdef0123456789abcdef").is_ok());
164        assert!(Digest::parse("balls:0123456789abcdef0123456789abcdeg").is_err());
165        assert!(Digest::parse("balls:0123456789abcdef0123456789abcdefF").is_ok());
166        assert!(Digest::parse("ball.ball.ball.balls:0123456789abcdef0123456789abcdef").is_ok());
167        assert!(Digest::parse("0123456789abcdef0123456789abcdef").is_ok());
168        assert!(Digest::parse(":0123456789abcdef0123456789abcdef").is_err());
169        assert!(Digest::parse("balls:0123456789abcdef0123456789abcde").is_err());
170        assert!(Digest::parse("b9:0123456789abcdef0123456789abcdef").is_ok());
171        assert!(Digest::parse("b:0123456789abcdef0123456789abcdef").is_ok());
172        assert!(Digest::parse("9:0123456789abcdef0123456789abcdef").is_err());
173        assert!(Digest::parse(" balls:0123456789abcdef0123456789abcdef").is_err());
174        assert!(Digest::parse("balls:0123456789abcdef0123456789abcdef ").is_err());
175    }
176}