use getset::{Getters, MutGetters, Setters};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use std::{collections::HashMap, fmt};
use crate::prelude::*;
#[derive(Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
#[getset(get = "pub", set = "pub")]
pub struct Metadata {
pub(super) title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) description: Option<String>,
#[get_mut = "pub"]
pub(super) authors: Vec<Author>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(alias = "custom_test_case_metadata")]
#[allow(
clippy::struct_field_names,
reason = "This field refers to the name of it's subtype"
)]
pub(super) custom_metadata: Option<HashMap<String, CustomMetadataField>>,
#[get = "pub"]
#[serde(flatten)]
pub(super) extra_fields: HashMap<String, serde_json::Value>,
}
impl Metadata {
#[allow(clippy::missing_panics_doc, reason = "safety is explained inline")]
pub fn custom_test_case_metadata_mut(&mut self) -> &mut HashMap<String, CustomMetadataField> {
if self.custom_metadata.is_none() {
self.custom_metadata = Some(HashMap::new());
}
self.custom_metadata.as_mut().unwrap()
}
pub fn insert_custom_metadata_field(
&mut self,
id: Option<String>,
name: String,
description: String,
make_primary: bool,
) -> (String, CustomMetadataField) {
let custom_fields = self.custom_test_case_metadata_mut();
if make_primary {
for (_key, item) in custom_fields.iter_mut() {
item.primary = false;
}
}
let new_id = id.unwrap_or_else(|| Uuid::now_v7().to_string());
let field = CustomMetadataField {
name,
description,
primary: make_primary,
extra_fields: HashMap::new(),
};
custom_fields.insert(new_id.clone(), field.clone());
(new_id, field)
}
}
#[derive(Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize, PartialEq, Eq)]
#[getset(get = "pub", get_mut = "pub", set = "pub")]
pub struct Author {
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
email: Option<String>,
#[get = "pub"]
#[serde(flatten)]
extra_fields: HashMap<String, serde_json::Value>,
}
impl Author {
pub fn new<S: Into<String>>(name: S) -> Self {
Self {
name: name.into(),
email: None,
extra_fields: HashMap::new(),
}
}
pub fn new_with_email<S: Into<String>>(name: S, email_address: S) -> Self {
Self {
name: name.into(),
email: Some(email_address.into()),
extra_fields: HashMap::new(),
}
}
}
impl fmt::Display for Author {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.email.is_some() {
write!(f, "{} <{}>", self.name, self.email.as_ref().unwrap())
} else {
write!(f, "{}", self.name)
}
}
}
impl<S1: Into<String>, S2: Into<String>> From<(S1, S2)> for Author {
fn from((name, email): (S1, S2)) -> Self {
Self {
name: name.into(),
email: Some(email.into()),
extra_fields: HashMap::new(),
}
}
}
#[derive(Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize, PartialEq, Eq)]
#[getset(get = "pub", get_mut = "pub", set = "pub")]
pub struct CustomMetadataField {
name: String,
description: String,
primary: bool,
#[get = "pub"]
#[serde(flatten)]
extra_fields: HashMap<String, serde_json::Value>,
}
impl PartialOrd for CustomMetadataField {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for CustomMetadataField {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
if self.primary && !other.primary {
return std::cmp::Ordering::Less;
}
if !self.primary && other.primary {
return std::cmp::Ordering::Greater;
}
self.name.cmp(&other.name)
}
}
#[derive(Clone, Debug, Getters, Serialize, Deserialize)]
#[getset(get = "pub")]
pub(super) struct MediaFileManifestEntry {
sha256_checksum: String,
mime_type: String,
#[get = "pub"]
#[serde(flatten)]
extra_fields: HashMap<String, serde_json::Value>,
}
impl From<&MediaFile> for MediaFileManifestEntry {
fn from(value: &MediaFile) -> Self {
Self {
sha256_checksum: value.hash(),
mime_type: value
.mime_type()
.map_or("unknown", |t| t.mime_type())
.to_string(),
extra_fields: HashMap::new(),
}
}
}
#[derive(Clone, Debug, Getters, Serialize, Deserialize)]
#[getset(get = "pub")]
pub(super) struct TestCaseManifestEntry {
#[serde(alias = "name")] pub(super) id: Uuid,
pub(super) attestations: Vec<String>,
#[get = "pub"]
#[serde(flatten)]
extra_fields: HashMap<String, serde_json::Value>,
}
impl TestCaseManifestEntry {
pub(super) fn new(id: Uuid) -> Self {
Self {
id,
attestations: vec![],
extra_fields: HashMap::new(),
}
}
}