use serde::de::{self, Deserialize, Deserializer, Unexpected};
use serde::ser::{Serialize, Serializer};
use std::borrow::Cow;
use std::fmt;
use std::marker::PhantomData;
pub mod apps;
pub mod core;
mod intstr;
pub mod meta;
pub type Time = String;
pub type Integer = i32;
pub use self::intstr::IntOrString;
pub type Quantity = String;
pub const JSON_PATCH: &'static str = "application/json-patch+json";
pub const MERGE_PATCH: &'static str = "application/merge-patch+json";
pub const STRATEGIC_MERGE_PATCH: &'static str = "application/strategic-merge-patch+json";
pub trait TypeMeta {
fn api_version() -> &'static str;
fn kind() -> &'static str;
}
#[derive(Default, Debug, Clone)]
pub struct TypeMetaStruct<T>(PhantomData<T>);
impl<T: TypeMeta> ::serde::de::Expected for TypeMetaStruct<T> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "{}/{}", T::api_version(), T::kind())
}
}
impl<T> PartialEq for TypeMetaStruct<T> {
fn eq(&self, _rhs: &Self) -> bool {
true
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename = "TypeMeta", rename_all = "camelCase")]
struct TypeMetaRuntime<'a> {
#[serde(borrow)]
api_version: Option<Cow<'a, str>>,
#[serde(borrow)]
kind: Option<Cow<'a, str>>,
}
impl<T: TypeMeta> Serialize for TypeMetaStruct<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let tmp = TypeMetaRuntime {
api_version: Some(Cow::from(T::api_version())),
kind: Some(Cow::from(T::kind())),
};
tmp.serialize(serializer)
}
}
impl<'de: 'a, 'a, T: TypeMeta> Deserialize<'de> for TypeMetaStruct<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let t = TypeMetaRuntime::deserialize(deserializer)?;
let ret = TypeMetaStruct(PhantomData);
match (t.api_version, t.kind) {
(Some(a), Some(k)) => {
if a == T::api_version() && k == T::kind() {
Ok(ret)
} else {
let found = format!("{}/{}", a, k);
Err(de::Error::invalid_value(Unexpected::Other(&found), &ret))
}
}
(None, None) => Ok(ret),
(Some(_), None) => Err(de::Error::missing_field("kind")),
(None, Some(_)) => Err(de::Error::missing_field("apiVersion")),
}
}
}
#[cfg(test)]
mod tests {
extern crate serde_test;
use self::serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token};
use super::*;
#[derive(Debug)]
struct TestType;
impl TypeMeta for TestType {
fn api_version() -> &'static str {
"v1alpha1"
}
fn kind() -> &'static str {
"Test"
}
}
#[test]
fn test_typemeta_serde() {
let t: TypeMetaStruct<TestType> = TypeMetaStruct(PhantomData);
assert_tokens(
&t,
&[
Token::Struct {
name: "TypeMeta",
len: 2,
},
Token::Str("apiVersion"),
Token::Some,
Token::BorrowedStr("v1alpha1"),
Token::Str("kind"),
Token::Some,
Token::BorrowedStr("Test"),
Token::StructEnd,
],
);
assert_de_tokens(
&t,
&[
Token::Struct {
name: "TypeMeta",
len: 2,
},
Token::Str("kind"),
Token::Some,
Token::BorrowedStr("Test"),
Token::Str("apiVersion"),
Token::Some,
Token::BorrowedStr("v1alpha1"),
Token::StructEnd,
],
);
assert_de_tokens(
&t,
&[
Token::Struct {
name: "TypeMeta",
len: 0,
},
Token::StructEnd,
],
);
}
#[test]
fn test_typemeta_serde_error() {
assert_de_tokens_error::<TypeMetaStruct<TestType>>(
&[
Token::Struct {
name: "TypeMeta",
len: 1,
},
Token::Str("kind"),
Token::Some,
Token::BorrowedStr("TestType"),
Token::StructEnd,
],
"missing field `apiVersion`",
);
assert_de_tokens_error::<TypeMetaStruct<TestType>>(
&[
Token::Struct {
name: "TypeMeta",
len: 1,
},
Token::Str("apiVersion"),
Token::Some,
Token::BorrowedStr("bogus"),
Token::StructEnd,
],
"missing field `kind`",
);
assert_de_tokens_error::<TypeMetaStruct<TestType>>(
&[
Token::Struct {
name: "TypeMeta",
len: 1,
},
Token::Str("apiVersion"),
Token::Some,
Token::Str("v1alpha1"),
Token::Str("apiVersion"),
Token::StructEnd,
],
"duplicate field `apiVersion`",
);
assert_de_tokens_error::<TypeMetaStruct<TestType>>(
&[
Token::Struct {
name: "TypeMeta",
len: 2,
},
Token::Str("kind"),
Token::Some,
Token::Str("NotTest"),
Token::Str("apiVersion"),
Token::Some,
Token::Str("v1alpha1"),
Token::StructEnd,
],
"invalid value: v1alpha1/NotTest, expected v1alpha1/Test",
);
}
}