Skip to main content

docker_registry_client/docker/
token.rs

1use chrono::{
2    DateTime,
3    Utc,
4};
5use reqwest::header::HeaderMap;
6use serde::{
7    Deserialize,
8    Serialize,
9};
10
11use crate::Image;
12
13#[derive(Debug, Eq, PartialEq, Hash)]
14pub(super) struct CacheKey {
15    image: Image,
16}
17
18#[derive(Debug, Default, Clone, Deserialize, Serialize)]
19pub(super) struct Token {
20    #[serde(rename = "token")]
21    pub(super) value: String,
22    pub(super) expires_in: Option<i64>,
23    pub(super) issued_at: Option<DateTime<Utc>>,
24}
25
26impl std::fmt::Display for CacheKey {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        let registry = self.image.registry.to_string();
29        let namespace = self.image.namespace.as_ref();
30        let repository = self.image.repository.as_ref();
31        let image_name = &self.image.image_name.name;
32
33        write!(f, "{registry}{namespace:?}{repository:?}{image_name}")
34    }
35}
36
37impl From<&Image> for CacheKey {
38    fn from(image: &Image) -> Self {
39        Self {
40            image: image.clone(),
41        }
42    }
43}
44
45impl TryInto<HeaderMap> for Token {
46    type Error = reqwest::header::InvalidHeaderValue;
47
48    fn try_into(self) -> Result<HeaderMap, Self::Error> {
49        let mut headers = HeaderMap::new();
50        headers.insert("Authorization", format!("Bearer {}", self.value).parse()?);
51
52        Ok(headers)
53    }
54}
55
56#[cfg(test)]
57#[expect(clippy::unwrap_used, reason = "using unwrap for tests is fine")]
58mod tests {
59    mod token {
60        mod deserialize {
61            use crate::docker::Token;
62
63            #[test]
64            fn github() {
65                const INPUT: &str =
66                    r#"{"token":"djE6c2lnc3RvcmUvY29zaWduL2Nvc2lnbjoxNzI1NDM2OTAwNTczODMyMzM2"}"#;
67
68                let got = serde_json::from_str::<Token>(INPUT).unwrap();
69                insta::assert_json_snapshot!(got);
70            }
71
72            #[test]
73            fn dockerhub() {
74                const INPUT: &str = r#"{"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlFRmpDQ0F2NmdBd0lCQWdJVVZOajJRbU1JWnUzeGl0NUJ1RTlvRWdoVU5KUXdEUVlKS29aSWh2Y05BUUVMQlFBd2dZWXhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saE1SSXdFQVlEVlFRSEV3bFFZV3h2SUVGc2RHOHhGVEFUQmdOVkJBb1RERVJ2WTJ0bGNpd2dTVzVqTGpFVU1CSUdBMVVFQ3hNTFJXNW5hVzVsWlhKcGJtY3hJVEFmQmdOVkJBTVRHRVJ2WTJ0bGNpd2dTVzVqTGlCRmJtY2dVbTl2ZENCRFFUQWVGdzB5TkRBeE1UWXdOak0yTURCYUZ3MHlOVEF4TVRVd05qTTJNREJhTUlHRk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQk1LUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnhNSlVHRnNieUJCYkhSdk1SVXdFd1lEVlFRS0V3eEViMk5yWlhJc0lFbHVZeTR4RkRBU0JnTlZCQXNUQzBWdVoybHVaV1Z5YVc1bk1TQXdIZ1lEVlFRREV4ZEViMk5yWlhJc0lFbHVZeTRnUlc1bklFcFhWQ0JEUVRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTWI4eHR6ZDQ1UWdYekV0bWMxUEJsdWNGUnlzSUF4UUJCN3lSNjdJemdMd05IS24rbUdKTzV5alh6amtLZm5zWm1JRURnZFlraEpBbGNYYTdQa1BFaCtqcTRGNWNaaWtkTmFUQmM3alNkTFJzTVlVa3dwWTl4WUVqYitCYnVGUWVxa0R2RXNqbFJJTzRQK0FsRlhNMDhMYlpIZ3hFWUdkbFk3WFlhT1BLMmE1aUd2eVFRb09GVmZjZDd2ekhaREVBMHZqVmU1M0xLdjVMYmh6TzcxZHRxS0RwNEhnVWR5N1pENDFNN3I1bTd5eE1LeFNpQmJHZTFvem5Wamh1ck5GNHdGSml5bVU4YkhTV2tVTXVLQ3JTbEd4d1NCZFVZNDRyaEh2UW5zYmgzUFF2TUZTWTQ4REdoNFhUUldjSzFWUVlSTnA2ZWFXUVg1RUpJSXVJbjJQOVBzQ0F3RUFBYU43TUhrd0RnWURWUjBQQVFIL0JBUURBZ0dtTUJNR0ExVWRKUVFNTUFvR0NDc0dBUVVGQndNQk1CSUdBMVVkRXdFQi93UUlNQVlCQWY4Q0FRQXdIUVlEVlIwT0JCWUVGSnVRYXZTZHVScm5kRXhLTTAwV2Z2czh5T0RaTUI4R0ExVWRJd1FZTUJhQUZGSGVwRE9ZQ0Y5Qnc5dXNsY0RVUW5CalU3MS9NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUNDWW0xUVorUUZ1RVhkSWpiNkg4bXNyVFBRSlNnR0JpWDFXSC9QRnpqZlJGeHc3dTdDazBRb0FXZVNqV3JWQWtlVlZQN3J2REpwZ0ZoeUljdzNzMXRPVjN0OGp3cXJTUmc2R285dUd2OG9IWUlLTm9YaDErUFVDTG44b0JwYUJsOUxzSWtsL2FHMG9lY0hrcDVLYmtBNjN6eTFxSUVXNFAzWVJLSk9hSGoxYWFiOXJLc3lRSHF6SUl4TnlDRVVINTMwU1B4RUNMbE53YWVKTDVmNXIxUW5wSi9GM3Q5Vk8xZ0Y2RFpiNitPczdTV29ocGhWZlRCOERkL1VjSk1VOGp2YlF3MWRVREkwelNEdXo2aHNJbGdITk0yak04M0lOS1VqNjNaRDMwRG15ejQvczFFdGgyQmlKK2RHdnFpQkRzaWhaR0tyQnJzUzhWVkRBd3hDeDVRMyJdfQ.eyJhY2Nlc3MiOlt7ImFjdGlvbnMiOlsicHVsbCJdLCJuYW1lIjoibGlicmFyeS9hbHBpbmUiLCJwYXJhbWV0ZXJzIjp7InB1bGxfbGltaXQiOiIxMDAiLCJwdWxsX2xpbWl0X2ludGVydmFsIjoiMjE2MDAifSwidHlwZSI6InJlcG9zaXRvcnkifV0sImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5pbyIsImV4cCI6MTcyNTQzNzIwMSwiaWF0IjoxNzI1NDM2OTAxLCJpc3MiOiJhdXRoLmRvY2tlci5pbyIsImp0aSI6ImRja3JfanRpX1dVVnQ2RldmdGRyNUU4UUNTTmwxb1M3NG1LUT0iLCJuYmYiOjE3MjU0MzY2MDEsInN1YiI6IiJ9.SMcP5s3bLnOmC0X3pG4b9N8PD3Dtpk5xovCzdcje-79O-lRx0Tqa2zruCauMENetmmq2CoxZOsY1ostvrAfZaDp2f_KkjaftymHhvgx60ppxIZuHIwZJjt9v7XprJuK8OZ1QxHL-HKp5nw8dRwa-IyScs-YevTz7PMD5B7uy36AZs1db2FwT_6Ygzhkvuq3OKgr70Mie2WHAoyShEJs7V6b9tywiSXPEgu2Nf7VSrj6PeZOi__IQl0wXo7w4j7C8o6ijUAotPRfMoQUXN1j9_4ql-yJZpp5jVmLsEOaOVKPjoIvnyDWYs9KVluUF4NKgvOydgIP-MeccXXIFOg4bhA","access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlFRmpDQ0F2NmdBd0lCQWdJVVZOajJRbU1JWnUzeGl0NUJ1RTlvRWdoVU5KUXdEUVlKS29aSWh2Y05BUUVMQlFBd2dZWXhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saE1SSXdFQVlEVlFRSEV3bFFZV3h2SUVGc2RHOHhGVEFUQmdOVkJBb1RERVJ2WTJ0bGNpd2dTVzVqTGpFVU1CSUdBMVVFQ3hNTFJXNW5hVzVsWlhKcGJtY3hJVEFmQmdOVkJBTVRHRVJ2WTJ0bGNpd2dTVzVqTGlCRmJtY2dVbTl2ZENCRFFUQWVGdzB5TkRBeE1UWXdOak0yTURCYUZ3MHlOVEF4TVRVd05qTTJNREJhTUlHRk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQk1LUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnhNSlVHRnNieUJCYkhSdk1SVXdFd1lEVlFRS0V3eEViMk5yWlhJc0lFbHVZeTR4RkRBU0JnTlZCQXNUQzBWdVoybHVaV1Z5YVc1bk1TQXdIZ1lEVlFRREV4ZEViMk5yWlhJc0lFbHVZeTRnUlc1bklFcFhWQ0JEUVRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTWI4eHR6ZDQ1UWdYekV0bWMxUEJsdWNGUnlzSUF4UUJCN3lSNjdJemdMd05IS24rbUdKTzV5alh6amtLZm5zWm1JRURnZFlraEpBbGNYYTdQa1BFaCtqcTRGNWNaaWtkTmFUQmM3alNkTFJzTVlVa3dwWTl4WUVqYitCYnVGUWVxa0R2RXNqbFJJTzRQK0FsRlhNMDhMYlpIZ3hFWUdkbFk3WFlhT1BLMmE1aUd2eVFRb09GVmZjZDd2ekhaREVBMHZqVmU1M0xLdjVMYmh6TzcxZHRxS0RwNEhnVWR5N1pENDFNN3I1bTd5eE1LeFNpQmJHZTFvem5Wamh1ck5GNHdGSml5bVU4YkhTV2tVTXVLQ3JTbEd4d1NCZFVZNDRyaEh2UW5zYmgzUFF2TUZTWTQ4REdoNFhUUldjSzFWUVlSTnA2ZWFXUVg1RUpJSXVJbjJQOVBzQ0F3RUFBYU43TUhrd0RnWURWUjBQQVFIL0JBUURBZ0dtTUJNR0ExVWRKUVFNTUFvR0NDc0dBUVVGQndNQk1CSUdBMVVkRXdFQi93UUlNQVlCQWY4Q0FRQXdIUVlEVlIwT0JCWUVGSnVRYXZTZHVScm5kRXhLTTAwV2Z2czh5T0RaTUI4R0ExVWRJd1FZTUJhQUZGSGVwRE9ZQ0Y5Qnc5dXNsY0RVUW5CalU3MS9NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUNDWW0xUVorUUZ1RVhkSWpiNkg4bXNyVFBRSlNnR0JpWDFXSC9QRnpqZlJGeHc3dTdDazBRb0FXZVNqV3JWQWtlVlZQN3J2REpwZ0ZoeUljdzNzMXRPVjN0OGp3cXJTUmc2R285dUd2OG9IWUlLTm9YaDErUFVDTG44b0JwYUJsOUxzSWtsL2FHMG9lY0hrcDVLYmtBNjN6eTFxSUVXNFAzWVJLSk9hSGoxYWFiOXJLc3lRSHF6SUl4TnlDRVVINTMwU1B4RUNMbE53YWVKTDVmNXIxUW5wSi9GM3Q5Vk8xZ0Y2RFpiNitPczdTV29ocGhWZlRCOERkL1VjSk1VOGp2YlF3MWRVREkwelNEdXo2aHNJbGdITk0yak04M0lOS1VqNjNaRDMwRG15ejQvczFFdGgyQmlKK2RHdnFpQkRzaWhaR0tyQnJzUzhWVkRBd3hDeDVRMyJdfQ.eyJhY2Nlc3MiOlt7ImFjdGlvbnMiOlsicHVsbCJdLCJuYW1lIjoibGlicmFyeS9hbHBpbmUiLCJwYXJhbWV0ZXJzIjp7InB1bGxfbGltaXQiOiIxMDAiLCJwdWxsX2xpbWl0X2ludGVydmFsIjoiMjE2MDAifSwidHlwZSI6InJlcG9zaXRvcnkifV0sImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5pbyIsImV4cCI6MTcyNTQzNzIwMSwiaWF0IjoxNzI1NDM2OTAxLCJpc3MiOiJhdXRoLmRvY2tlci5pbyIsImp0aSI6ImRja3JfanRpX1dVVnQ2RldmdGRyNUU4UUNTTmwxb1M3NG1LUT0iLCJuYmYiOjE3MjU0MzY2MDEsInN1YiI6IiJ9.SMcP5s3bLnOmC0X3pG4b9N8PD3Dtpk5xovCzdcje-79O-lRx0Tqa2zruCauMENetmmq2CoxZOsY1ostvrAfZaDp2f_KkjaftymHhvgx60ppxIZuHIwZJjt9v7XprJuK8OZ1QxHL-HKp5nw8dRwa-IyScs-YevTz7PMD5B7uy36AZs1db2FwT_6Ygzhkvuq3OKgr70Mie2WHAoyShEJs7V6b9tywiSXPEgu2Nf7VSrj6PeZOi__IQl0wXo7w4j7C8o6ijUAotPRfMoQUXN1j9_4ql-yJZpp5jVmLsEOaOVKPjoIvnyDWYs9KVluUF4NKgvOydgIP-MeccXXIFOg4bhA","expires_in":300,"issued_at":"2024-09-04T08:01:41.048681016Z"}"#;
75
76                let got = serde_json::from_str::<Token>(INPUT).unwrap();
77                insta::assert_json_snapshot!(got);
78            }
79        }
80    }
81}