use crate::{
api::{typed::Api, Resource},
Client,
};
use inflector::{cases::pascalcase::is_pascal_case, string::pluralize::to_plural};
use std::marker::PhantomData;
pub struct CustomResource {
kind: String,
group: String,
version: String,
api_version: String,
namespace: Option<String>,
}
impl CustomResource {
pub fn kind(kind: &str) -> CrBuilder {
CrBuilder::kind(kind)
}
}
#[derive(Default)]
pub struct CrBuilder {
pub(crate) kind: String,
pub(crate) version: Option<String>,
pub(crate) group: Option<String>,
pub(crate) namespace: Option<String>,
}
impl CrBuilder {
fn kind(kind: &str) -> Self {
assert!(to_plural(kind) != kind); assert!(is_pascal_case(&kind)); Self {
kind: kind.into(),
..Default::default()
}
}
pub fn group(mut self, group: &str) -> Self {
self.group = Some(group.to_string());
self
}
pub fn version(mut self, version: &str) -> Self {
self.version = Some(version.to_string());
self
}
pub fn within(mut self, ns: &str) -> Self {
self.namespace = Some(ns.into());
self
}
pub fn build(self) -> CustomResource {
let version = self.version.expect("Crd must have a version");
let group = self.group.expect("Crd must have a group");
CustomResource {
api_version: format!("{}/{}", group, version),
kind: self.kind,
version,
group,
namespace: self.namespace,
}
}
pub fn into_api<K>(self, client: Client) -> Api<K> {
let crd = self.build();
Api {
client,
resource: crd.into(),
phantom: PhantomData,
}
}
pub fn into_resource(self) -> Resource {
let crd = self.build();
crd.into()
}
}
impl From<CustomResource> for Resource {
fn from(c: CustomResource) -> Self {
Self {
api_version: c.api_version,
kind: c.kind,
group: c.group,
version: c.version,
namespace: c.namespace,
}
}
}
impl CustomResource {
pub fn into_api<K>(self, client: Client) -> Api<K> {
Api {
client,
resource: self.into(),
phantom: PhantomData,
}
}
}
#[cfg(test)]
mod test {
use crate::api::{CustomResource, PatchParams, PostParams, Resource};
#[test]
fn raw_custom_resource() {
let r: Resource = CustomResource::kind("Foo")
.group("clux.dev")
.version("v1")
.within("myns")
.into_resource();
let pp = PostParams::default();
let req = r.create(&pp, vec![]).unwrap();
assert_eq!(req.uri(), "/apis/clux.dev/v1/namespaces/myns/foos?");
let patch_params = PatchParams::default();
let req = r.patch("baz", &patch_params, vec![]).unwrap();
assert_eq!(req.uri(), "/apis/clux.dev/v1/namespaces/myns/foos/baz?");
assert_eq!(req.method(), "PATCH");
}
#[tokio::test]
#[ignore] async fn convenient_custom_resource() {
use crate::{Api, Client};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, kube_derive::CustomResource, Deserialize, Serialize)]
#[kube(group = "clux.dev", version = "v1", namespaced)]
struct FooSpec {
foo: String,
};
let client = Client::try_default().await.unwrap();
let a1: Api<Foo> = Api::namespaced(client.clone(), "myns");
let a2: Api<Foo> = CustomResource::kind("Foo")
.group("clux.dev")
.version("v1")
.within("myns")
.build()
.into_api(client);
assert_eq!(a1.resource.api_version, a2.resource.api_version);
}
}