use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, iter::*};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct Annotations {
#[serde(rename = "org.opencontainers.image.created")]
#[serde(skip_serializing_if = "Option::is_none")]
pub created: Option<String>,
#[serde(rename = "org.opencontainers.image.authors")]
#[serde(skip_serializing_if = "Option::is_none")]
pub authors: Option<String>,
#[serde(rename = "org.opencontainers.image.url")]
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(rename = "org.opencontainers.image.documentation")]
#[serde(skip_serializing_if = "Option::is_none")]
pub documentation: Option<String>,
#[serde(rename = "org.opencontainers.image.source")]
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
#[serde(rename = "org.opencontainers.image.version")]
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(rename = "org.opencontainers.image.revision")]
#[serde(skip_serializing_if = "Option::is_none")]
pub revision: Option<String>,
#[serde(rename = "org.opencontainers.image.vendor")]
#[serde(skip_serializing_if = "Option::is_none")]
pub vendor: Option<String>,
#[serde(rename = "org.opencontainers.image.licenses")]
#[serde(skip_serializing_if = "Option::is_none")]
pub licenses: Option<String>,
#[serde(rename = "org.opencontainers.image.ref.name")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ref_name: Option<String>,
#[serde(rename = "org.opencontainers.image.title")]
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(rename = "org.opencontainers.image.description")]
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(rename = "org.opencontainers.image.base.digest")]
#[serde(skip_serializing_if = "Option::is_none")]
pub base_digest: Option<String>,
#[serde(rename = "org.opencontainers.image.base.name")]
#[serde(skip_serializing_if = "Option::is_none")]
pub base_name: Option<String>,
}
impl Annotations {
pub fn from_map(annotations: HashMap<String, String>) -> Result<Self> {
let map = serde_json::Map::from_iter(
annotations
.into_iter()
.map(|(key, value)| (key, value.into())),
);
Ok(serde_json::from_value(map.into())?)
}
pub fn from_json(input: &str) -> Result<Self> {
Ok(serde_json::from_str(input)?)
}
pub fn to_map(&self) -> HashMap<String, String> {
use serde_json::Value;
let json = serde_json::to_value(self).unwrap();
if let Value::Object(map) = json {
map.into_iter()
.map(|(key, value)| {
if let serde_json::Value::String(value) = value {
(key, value)
} else {
unreachable!()
}
})
.collect()
} else {
unreachable!()
}
}
pub fn to_json(&self) -> String {
serde_json::to_string_pretty(self).unwrap()
}
}
impl std::iter::FromIterator<(String, String)> for Annotations {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = (String, String)>,
{
let map =
serde_json::Map::from_iter(iter.into_iter().map(|(key, value)| (key, value.into())));
serde_json::from_value(map.into()).unwrap()
}
}
impl From<super::nested::Annotations> for Annotations {
fn from(nested: super::nested::Annotations) -> Self {
let ref_name = nested.r#ref.and_then(|r| r.name);
let (base_name, base_digest) = if let Some(base) = nested.base {
(base.name, base.digest)
} else {
(None, None)
};
Annotations {
created: nested.created,
authors: nested.authors,
url: nested.url,
documentation: nested.documentation,
description: nested.description,
title: nested.title,
source: nested.source,
version: nested.version,
revision: nested.revision,
vendor: nested.vendor,
licenses: nested.licenses,
ref_name,
base_name,
base_digest,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn to_map() {
let a = Annotations {
url: Some("https://github.com/termoshtt/ocipkg".to_string()),
..Default::default()
};
assert_eq!(
a.to_map(),
maplit::hashmap!(
"org.opencontainers.image.url".to_string()
=> "https://github.com/termoshtt/ocipkg".to_string(),
)
);
}
}