use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
fn epoch() -> DateTime<Utc> {
Default::default()
}
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
pub struct NonScopedMetadata {
pub name: String,
#[serde(default)]
#[serde(skip_serializing_if = "String::is_empty")]
pub uid: String,
#[serde(default = "epoch")]
pub creation_timestamp: DateTime<Utc>,
#[serde(default)]
pub generation: u64,
#[serde(default)]
#[serde(skip_serializing_if = "String::is_empty")]
pub resource_version: String,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub deletion_timestamp: Option<DateTime<Utc>>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub finalizers: Vec<String>,
#[serde(default)]
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub labels: HashMap<String, String>,
#[serde(default)]
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub annotations: HashMap<String, String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
pub struct ScopedMetadata {
pub application: String,
pub name: String,
#[serde(default)]
#[serde(skip_serializing_if = "String::is_empty")]
pub uid: String,
#[serde(default = "epoch")]
pub creation_timestamp: DateTime<Utc>,
#[serde(default)]
pub generation: u64,
#[serde(default)]
#[serde(skip_serializing_if = "String::is_empty")]
pub resource_version: String,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub deletion_timestamp: Option<DateTime<Utc>>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub finalizers: Vec<String>,
#[serde(default)]
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub labels: HashMap<String, String>,
#[serde(default)]
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub annotations: HashMap<String, String>,
}
impl Default for ScopedMetadata {
fn default() -> Self {
Self {
application: Default::default(),
name: Default::default(),
uid: Default::default(),
labels: Default::default(),
annotations: Default::default(),
creation_timestamp: Utc::now(),
resource_version: Default::default(),
generation: Default::default(),
deletion_timestamp: Default::default(),
finalizers: Default::default(),
}
}
}
impl Default for NonScopedMetadata {
fn default() -> Self {
Self {
name: Default::default(),
uid: Default::default(),
labels: Default::default(),
annotations: Default::default(),
creation_timestamp: Utc::now(),
resource_version: Default::default(),
generation: Default::default(),
deletion_timestamp: Default::default(),
finalizers: Default::default(),
}
}
}
pub trait CommonMetadata {
fn name(&self) -> &String;
fn uid(&self) -> &String;
fn labels(&self) -> &HashMap<String, String>;
fn annotations(&self) -> &HashMap<String, String>;
fn creation_timestamp(&self) -> &DateTime<Utc>;
fn resource_version(&self) -> &String;
fn generation(&self) -> u64;
fn deletion_timestamp(&self) -> &Option<DateTime<Utc>>;
fn finalizers(&self) -> &Vec<String>;
}
pub trait CommonMetadataMut: CommonMetadata {
fn set_name(&mut self, name: String);
fn set_uid(&mut self, uid: String);
fn set_labels(&mut self, labels: HashMap<String, String>);
fn set_annotations(&mut self, annotations: HashMap<String, String>);
fn set_creation_timestamp(&mut self, creation_timestamp: DateTime<Utc>);
fn set_resource_version(&mut self, resource_version: String);
fn set_generation(&mut self, generation: u64);
fn set_deletion_timestamp(&mut self, deletion_timestamp: Option<DateTime<Utc>>);
fn set_finalizers(&mut self, finalizers: Vec<String>);
fn name_mut(&mut self) -> &mut String;
fn uid_mut(&mut self) -> &mut String;
fn labels_mut(&mut self) -> &mut HashMap<String, String>;
fn annotations_mut(&mut self) -> &mut HashMap<String, String>;
fn creation_timestamp_mut(&mut self) -> &mut DateTime<Utc>;
fn resource_version_mut(&mut self) -> &mut String;
fn generation_mut(&mut self) -> &mut u64;
fn deletion_timestamp_mut(&mut self) -> &mut Option<DateTime<Utc>>;
fn finalizers_mut(&mut self) -> &mut Vec<String>;
fn ensure_finalizer(&mut self, finalizer: &str) -> bool {
let finalizers = self.finalizers_mut();
if !finalizers.iter().any(|r| r == finalizer) {
finalizers.push(finalizer.into());
true
} else {
false
}
}
fn remove_finalizer(&mut self, finalizer: &str) -> bool {
let mut found = false;
self.finalizers_mut().retain(|f| match f == finalizer {
true => {
found = true;
false
}
false => true,
});
found
}
}
pub trait CommonMetadataExt {
fn has_label<L: AsRef<str>>(&self, label: L) -> bool;
fn has_label_flag<L: AsRef<str>>(&self, label: L) -> bool;
}
impl<C: CommonMetadata> CommonMetadataExt for C {
fn has_label<L: AsRef<str>>(&self, label: L) -> bool {
self.labels().contains_key(label.as_ref())
}
fn has_label_flag<L: AsRef<str>>(&self, label: L) -> bool {
self.labels()
.get(label.as_ref())
.map(|v| v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
}
}
macro_rules! common_metadata {
($name:ty) => {
impl CommonMetadata for $name {
fn name(&self) -> &String {
&self.name
}
fn uid(&self) -> &String {
&self.uid
}
fn labels(&self) -> &HashMap<String, String> {
&self.labels
}
fn annotations(&self) -> &HashMap<String, String> {
&self.annotations
}
fn creation_timestamp(&self) -> &DateTime<chrono::Utc> {
&self.creation_timestamp
}
fn resource_version(&self) -> &String {
&self.resource_version
}
fn generation(&self) -> u64 {
self.generation
}
fn deletion_timestamp(&self) -> &Option<DateTime<chrono::Utc>> {
&self.deletion_timestamp
}
fn finalizers(&self) -> &Vec<String> {
&self.finalizers
}
}
impl CommonMetadataMut for $name {
fn set_name(&mut self, name: String) {
self.name = name;
}
fn set_uid(&mut self, uid: String) {
self.uid = uid;
}
fn set_labels(&mut self, labels: HashMap<String, String>) {
self.labels = labels;
}
fn set_annotations(&mut self, annotations: HashMap<String, String>) {
self.annotations = annotations;
}
fn set_creation_timestamp(&mut self, creation_timestamp: DateTime<Utc>) {
self.creation_timestamp = creation_timestamp;
}
fn set_resource_version(&mut self, resource_version: String) {
self.resource_version = resource_version;
}
fn set_generation(&mut self, generation: u64) {
self.generation = generation;
}
fn set_deletion_timestamp(&mut self, deletion_timestamp: Option<DateTime<Utc>>) {
self.deletion_timestamp = deletion_timestamp;
}
fn set_finalizers(&mut self, finalizers: Vec<String>) {
self.finalizers = finalizers;
}
fn name_mut(&mut self) -> &mut String {
&mut self.name
}
fn uid_mut(&mut self) -> &mut String {
&mut self.uid
}
fn labels_mut(&mut self) -> &mut HashMap<String, String> {
&mut self.labels
}
fn annotations_mut(&mut self) -> &mut HashMap<String, String> {
&mut self.annotations
}
fn creation_timestamp_mut(&mut self) -> &mut DateTime<chrono::Utc> {
&mut self.creation_timestamp
}
fn resource_version_mut(&mut self) -> &mut String {
&mut self.resource_version
}
fn generation_mut(&mut self) -> &mut u64 {
&mut self.generation
}
fn deletion_timestamp_mut(&mut self) -> &mut Option<DateTime<chrono::Utc>> {
&mut self.deletion_timestamp
}
fn finalizers_mut(&mut self) -> &mut Vec<String> {
&mut self.finalizers
}
}
};
}
common_metadata!(ScopedMetadata);
common_metadata!(NonScopedMetadata);
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_epoch() {
assert_eq!(0, epoch().timestamp_millis());
}
#[test]
fn test_set() {
let mut meta = ScopedMetadata::default();
let meta_ref: &mut dyn CommonMetadataMut = &mut meta;
let ts = Utc::now();
let mut labels = HashMap::new();
labels.insert("foo".to_string(), "bar".to_string());
meta_ref.set_name("foo".into());
meta_ref.set_deletion_timestamp(Some(ts.clone()));
meta_ref.set_labels(labels.clone());
assert_eq!(meta.name, "foo");
assert_eq!(meta.deletion_timestamp, Some(ts));
assert_eq!(meta.labels, labels);
}
#[test]
fn test_finalizer() {
let mut meta = ScopedMetadata::default();
meta.ensure_finalizer("Foo");
assert_eq!(meta.finalizers, vec!["Foo"]);
{
let meta_ref: &mut dyn CommonMetadataMut = &mut meta;
meta_ref.ensure_finalizer("Bar");
}
assert_eq!(meta.finalizers, vec!["Foo".to_string(), "Bar".to_string()]);
{
let meta_ref: &mut dyn CommonMetadataMut = &mut meta;
assert!(!meta_ref.remove_finalizer("Abc"));
assert!(meta_ref.remove_finalizer("Foo"));
}
assert_eq!(meta.finalizers, vec!["Bar".to_string()]);
}
}