mod json_ext;
pub(crate) mod object_id;
use crate::k8s_types::K8sType;
use serde_json::Value;
use std::fmt::{self, Debug};
pub use self::json_ext::ResourceJson;
pub use self::object_id::{ObjectId, ObjectIdRef};
pub type JsonObject = serde_json::Map<String, Value>;
#[derive(PartialEq, Clone)]
pub struct InvalidResourceError {
pub message: &'static str,
pub value: Value,
}
impl InvalidResourceError {
pub fn new(message: &'static str, value: Value) -> Self {
InvalidResourceError { message, value }
}
}
impl Debug for InvalidResourceError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"InvalidResourceError('{}', {})",
self.message, self.value
)
}
}
impl fmt::Display for InvalidResourceError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Invalid Resource: {}", self.message)
}
}
impl std::error::Error for InvalidResourceError {}
#[derive(PartialEq, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct K8sResource(Value);
impl Debug for K8sResource {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "K8sResource({})", self.0)
}
}
impl std::fmt::Display for K8sResource {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl K8sResource {
pub fn from_value(value: Value) -> Result<K8sResource, InvalidResourceError> {
if let Err(msg) = K8sResource::validate(&value) {
Err(InvalidResourceError {
message: msg,
value,
})
} else {
Ok(K8sResource(value))
}
}
pub fn into_type<T: serde::de::DeserializeOwned>(self) -> Result<T, serde_json::Error> {
serde_json::from_value(self.into_value())
}
pub fn into_value(self) -> Value {
self.0
}
pub fn is_id(&self, id: &ObjectIdRef) -> bool {
self.get_object_id() == *id
}
pub fn is_type(&self, k8s_type: &K8sType) -> bool {
self.api_version() == k8s_type.api_version && self.kind() == k8s_type.kind
}
pub fn resource_version(&self) -> &str {
self.str_value("/metadata/resourceVersion").unwrap()
}
pub fn get_label_value(&self, label: &str) -> Option<&str> {
let labels = self.0.pointer("/metadata/labels")?.as_object()?;
labels.get(label).and_then(Value::as_str)
}
pub fn uid(&self) -> &str {
self.str_value("/metadata/uid").unwrap()
}
pub fn name(&self) -> &str {
self.str_value("/metadata/name").unwrap()
}
pub fn namespace(&self) -> Option<&str> {
self.str_value("/metadata/namespace")
}
pub fn api_version(&self) -> &str {
self.str_value("/apiVersion").unwrap()
}
pub fn kind(&self) -> &str {
self.str_value("/kind").unwrap()
}
pub fn status(&self) -> Option<&Value> {
self.0.pointer("/status")
}
pub fn get_object_id(&self) -> ObjectIdRef {
let ns = self.namespace().unwrap_or("");
let name = self.name();
ObjectIdRef::new(ns, name)
}
pub fn get_type_ref(&self) -> K8sTypeRef {
let api_version = self.api_version();
let kind = self.kind();
K8sTypeRef(api_version, kind)
}
pub fn generation(&self) -> i64 {
self.0
.pointer("/metadata/generation")
.and_then(Value::as_i64)
.unwrap_or(-1)
}
pub fn is_deletion_timestamp_set(&self) -> bool {
self.0.pointer("/metadata/deletionTimestamp").is_some()
}
fn validate(value: &Value) -> Result<(), &'static str> {
value
.pointer("/metadata/resourceVersion")
.ok_or("missing metadata.resourceVersion")?;
value
.pointer("/metadata/name")
.ok_or("missing metadata.name")?;
value
.pointer("/metadata/uid")
.ok_or("missing metadata.uid")?;
value.pointer("/apiVersion").ok_or("missing apiVersion")?;
value.pointer("/kind").ok_or("missing kind")?;
Ok(())
}
pub fn str_value(&self, pointer: &str) -> Option<&str> {
self.0.pointer(pointer).and_then(Value::as_str)
}
}
impl json_ext::ResourceJson for K8sResource {
fn get_api_version(&self) -> Option<&str> {
Some(self.api_version())
}
fn get_kind(&self) -> Option<&str> {
Some(self.kind())
}
fn get_namespace(&self) -> Option<&str> {
self.namespace()
}
fn get_name(&self) -> Option<&str> {
Some(self.name())
}
}
impl std::convert::AsRef<Value> for K8sResource {
fn as_ref(&self) -> &Value {
&self.0
}
}
impl std::ops::Deref for K8sResource {
type Target = Value;
fn deref(&self) -> &Value {
&self.0
}
}
impl std::borrow::Borrow<Value> for K8sResource {
fn borrow(&self) -> &Value {
&self.0
}
}
impl Into<Value> for K8sResource {
fn into(self) -> Value {
self.into_value()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
pub struct K8sTypeRef<'a>(pub &'a str, pub &'a str);
impl<'a> K8sTypeRef<'a> {
pub fn new(api_version: &'a str, kind: &'a str) -> Self {
K8sTypeRef(api_version, kind)
}
pub fn as_parts(&self) -> (&str, &str) {
(self.0, self.1)
}
pub fn api_version(&self) -> &str {
self.as_parts().0
}
pub fn kind(&self) -> &str {
self.as_parts().1
}
}
impl<'a> std::fmt::Display for K8sTypeRef<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}/{}", self.0, self.1)
}
}
impl<'a> std::cmp::PartialEq<K8sType> for K8sTypeRef<'a> {
fn eq(&self, rhs: &K8sType) -> bool {
self.api_version() == rhs.api_version && self.kind() == rhs.kind
}
}
impl<'a> From<&'_ K8sType> for K8sTypeRef<'a> {
fn from(k8s_type: &K8sType) -> K8sTypeRef<'static> {
K8sTypeRef(k8s_type.api_version, k8s_type.kind)
}
}
impl<'a> From<(&'a str, &'a str)> for K8sTypeRef<'a> {
fn from((api_version, kind): (&'a str, &'a str)) -> K8sTypeRef<'a> {
K8sTypeRef(api_version, kind)
}
}