use std::collections::HashMap;
use log::trace;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use crate::errors::Result;
pub use crate::v2::ApiErrors;
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ManifestSchema2Spec {
#[serde(rename = "schemaVersion")]
schema_version: u16,
#[serde(rename = "mediaType")]
media_type: String,
config: Config,
layers: Vec<S2Layer>,
}
#[derive(Clone, Debug, Default)]
pub struct ManifestSchema2 {
pub manifest_spec: ManifestSchema2Spec,
pub config_blob: ConfigBlob,
}
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
pub struct Config {
#[serde(rename = "mediaType")]
pub media_type: String,
pub size: u64,
pub digest: String,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ConfigBlob {
architecture: String,
config: InnerConfigBlob,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
struct InnerConfigBlob {
#[serde(rename = "Labels")]
labels: Option<HashMap<String, String>>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
struct S2Layer {
#[serde(rename = "mediaType")]
media_type: String,
size: u64,
digest: String,
urls: Option<Vec<String>>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ManifestList {
#[serde(rename = "schemaVersion")]
schema_version: u16,
#[serde(rename = "mediaType")]
media_type: String,
pub manifests: Vec<ManifestObj>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ManifestObj {
#[serde(rename = "mediaType")]
media_type: String,
size: u64,
pub digest: String,
pub platform: Platform,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Platform {
pub architecture: String,
pub os: String,
#[serde(rename = "os.version")]
pub os_version: Option<String>,
#[serde(rename = "os.features")]
pub os_features: Option<Vec<String>>,
pub variant: Option<String>,
pub features: Option<Vec<String>>,
}
impl ManifestSchema2Spec {
pub fn config(&self) -> &Config {
&self.config
}
pub(crate) async fn fetch_config_blob(self, client: crate::v2::Client, repo: String) -> Result<ManifestSchema2> {
let url = {
let ep = format!("{}/v2/{}/blobs/{}", client.base_url.clone(), repo, self.config.digest);
reqwest::Url::parse(&ep)?
};
let r = client.build_reqwest(Method::GET, url.clone()).send().await?;
let status = r.status();
trace!("GET {:?}: {}", url, &status);
if !status.is_success() {
return Err(ApiErrors::from(r).await);
}
let config_blob = r.json::<ConfigBlob>().await?;
Ok(ManifestSchema2 {
manifest_spec: self,
config_blob,
})
}
}
impl ManifestSchema2 {
pub fn get_layers(&self) -> Vec<String> {
self.manifest_spec.layers.iter().map(|l| l.digest.clone()).collect()
}
pub fn architecture(&self) -> String {
self.config_blob.architecture.to_owned()
}
pub fn labels(&self) -> Option<HashMap<String, String>> {
self.config_blob.config.labels.to_owned()
}
}
impl ManifestObj {
pub fn architecture(&self) -> String {
self.platform.architecture.to_owned()
}
pub fn digest(&self) -> String {
self.digest.to_owned()
}
}
impl ManifestList {
pub fn architectures(&self) -> Vec<String> {
self.manifests.iter().map(|mo| mo.architecture()).collect()
}
pub fn get_digests(&self) -> Vec<String> {
self.manifests.iter().map(|mo| mo.digest()).collect()
}
}