github_workflows_update/updater/
docker.rs

1// Copyright (C) 2022 Leandro Lisboa Penz <lpenz@lpenz.org>
2// This file is subject to the terms and conditions defined in
3// file 'LICENSE', which is part of this source code package.
4
5use tracing::instrument;
6use url::Url;
7
8use crate::error::Error;
9use crate::error::Result;
10use crate::version::Version;
11
12#[instrument(level = "debug")]
13async fn get_json(url: &Url) -> Result<serde_json::Value> {
14    let response = reqwest::get(url.as_str()).await?;
15    if !response.status().is_success() {
16        return Err(Error::HttpError(url.clone(), response.status()));
17    }
18    Ok(response.json::<serde_json::Value>().await?)
19}
20
21#[instrument(level = "debug")]
22fn parse_versions(data: serde_json::Value) -> Result<Vec<Version>> {
23    data.as_object()
24        .ok_or_else(|| Error::JsonParsing("invalid type for top object".into()))?
25        .get("results")
26        .ok_or_else(|| Error::JsonParsing("could not find \"results\" member".into()))?
27        .as_array()
28        .ok_or_else(|| Error::JsonParsing("invalid type for \"results\" list".into()))?
29        .iter()
30        .map(|result| {
31            result
32                .as_object()
33                .ok_or_else(|| Error::JsonParsing("invalid type for \"result\" object".into()))?
34                .get("name")
35                .ok_or_else(|| {
36                    Error::JsonParsing("\"name\" field not found in \"result\" object".into())
37                })
38                .map(|version_value| {
39                    let version_str = version_value.as_str().ok_or_else(|| {
40                        Error::JsonParsing(
41                            "invalid type for \"name\" field in \"result\" object".into(),
42                        )
43                    })?;
44                    Version::new(version_str)
45                        .ok_or_else(|| Error::VersionParsing(version_str.into()))
46                })?
47        })
48        .collect::<Result<Vec<Version>>>()
49}
50
51#[instrument(level = "debug")]
52pub async fn get_versions(url: &Url) -> Result<Vec<Version>> {
53    let data = get_json(url).await?;
54    let versions = parse_versions(data)?;
55    Ok(versions)
56}
57
58#[test]
59fn test_docker_parse_versions() -> Result<()> {
60    let json_str = r#"{"results":[{"name": "latest"}, {"name": "0.2"}, {"name": "0.3"}, {"name": "0.4"}, {"name": "0.6"}, {"name": "0.7"}, {"name": "0.8.0"}, {"name": "0.9.0"}]}"#;
61    let json_value: serde_json::Value = serde_json::from_str(json_str)?;
62    let versions = parse_versions(json_value)?
63        .into_iter()
64        .map(|v| format!("{}", v))
65        .collect::<Vec<_>>();
66    assert_eq!(
67        versions,
68        [
69            "latest", "0.2", "0.3", "0.4", "0.6", "0.7", "0.8.0", "0.9.0"
70        ]
71    );
72    Ok(())
73}