1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use async_trait::async_trait;
use tracing::instrument;
use crate::entity::Entity;
use crate::error::Error;
use crate::error::Result;
use crate::updater;
use crate::version::Version;
#[derive(Debug, Default)]
pub struct Docker {}
#[async_trait]
impl updater::Updater for Docker {
fn url(&self, resource: &str) -> Option<String> {
url(resource)
}
async fn get_versions(&self, url: &str) -> Result<Vec<Version>> {
get_versions(url).await
}
fn updated_line(&self, entity: &Entity) -> Option<String> {
entity
.latest
.as_ref()
.map(|v| format!("{}:{}", entity.resource, v))
}
}
#[instrument(level = "debug")]
pub fn url(resource: &str) -> Option<String> {
resource.strip_prefix("docker://").map(|path| {
format!(
"https://registry.hub.docker.com/v1/repositories/{}/tags",
path
)
})
}
#[instrument(level = "debug")]
async fn get_json(url: &str) -> Result<serde_json::Value> {
let response = reqwest::get(url).await?;
if !response.status().is_success() {
return Err(Error::HttpError(url.into(), response.status()));
}
Ok(response.json::<serde_json::Value>().await?)
}
#[instrument(level = "debug")]
fn parse_versions(data: serde_json::Value) -> Result<Vec<Version>> {
data.as_array()
.ok_or_else(|| Error::JsonParsing("invalid type for layer object list".into()))?
.iter()
.map(|layer| {
layer
.as_object()
.ok_or_else(|| Error::JsonParsing("invalid type for layer object".into()))?
.get("name")
.ok_or_else(|| {
Error::JsonParsing("\"name\" field not found in layer object".into())
})
.map(|version_value| {
let version_str = version_value.as_str().ok_or_else(|| {
Error::JsonParsing("invalid type for \"name\" field in layer object".into())
})?;
Version::new(version_str)
.ok_or_else(|| Error::VersionParsing(version_str.into()))
})?
})
.collect::<Result<Vec<Version>>>()
}
#[instrument(level = "debug")]
pub async fn get_versions(url: &str) -> Result<Vec<Version>> {
let data = get_json(url).await?;
let versions = parse_versions(data)?;
Ok(versions)
}
#[test]
fn test_docker_parse_versions() -> Result<()> {
let json_str = r#"[{"layer": "", "name": "latest"}, {"layer": "", "name": "0.2"}, {"layer": "", "name": "0.3"}, {"layer": "", "name": "0.4"}, {"layer": "", "name": "0.6"}, {"layer": "", "name": "0.7"}, {"layer": "", "name": "0.8.0"}, {"layer": "", "name": "0.9.0"}]"#;
let json_value: serde_json::Value = serde_json::from_str(json_str)?;
let versions = parse_versions(json_value)?
.into_iter()
.map(|v| format!("{}", v))
.collect::<Vec<_>>();
assert_eq!(
versions,
["latest", "0.2", "0.3", "0.4", "0.6", "0.7", "0.8.0", "0.9.0"]
);
Ok(())
}