kube-derive 3.1.0

Custom derives for the kube kubernetes crates
Documentation
// Generated by darling macros, out of our control
#![allow(clippy::manual_unwrap_or_default)]

use darling::{FromDeriveInput, FromMeta};
use syn::{Data, DeriveInput, Path, parse_quote};

/// Values we can parse from #[kube(attrs)]
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(resource))]
struct InheritAttrs {
    inherit: syn::Path,
    #[darling(default)]
    crates: Crates,
}

#[derive(Debug, FromMeta)]
struct Crates {
    #[darling(default = "Self::default_kube_core")]
    kube_core: Path,
    #[darling(default = "Self::default_k8s_openapi")]
    k8s_openapi: Path,
}

// Default is required when the subattribute isn't mentioned at all
// Delegate to darling rather than deriving, so that we can piggyback off the `#[darling(default)]` clauses
impl Default for Crates {
    fn default() -> Self {
        Self::from_list(&[]).unwrap()
    }
}

impl Crates {
    fn default_kube_core() -> Path {
        parse_quote! { ::kube::core } // by default must work well with people using facade crate
    }

    fn default_k8s_openapi() -> Path {
        parse_quote! { ::k8s_openapi }
    }
}

pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
    let derive_input: DeriveInput = match syn::parse2(input) {
        Err(err) => return err.to_compile_error(),
        Ok(di) => di,
    };
    // Limit derive to structs
    match derive_input.data {
        Data::Struct(_) | Data::Enum(_) => {}
        _ => {
            return syn::Error::new_spanned(&derive_input.ident, r#"Unions can not #[derive(Resource)]"#)
                .to_compile_error();
        }
    }
    let kube_attrs = match InheritAttrs::from_derive_input(&derive_input) {
        Err(err) => return err.write_errors(),
        Ok(attrs) => attrs,
    };

    let InheritAttrs {
        inherit: resource,
        crates: Crates {
            kube_core,
            k8s_openapi,
        },
        ..
    } = kube_attrs;

    let rootident = derive_input.ident;

    let inherit_resource = quote! {
        impl #kube_core::Resource for #rootident {
            type DynamicType = <#resource as #kube_core::Resource>::DynamicType;
            type Scope = <#resource as #kube_core::Resource>::Scope;

            fn group(_: &<#resource as #kube_core::Resource>::DynamicType) -> std::borrow::Cow<'_, str> {
                #resource::group(&Default::default()).into_owned().into()
            }

            fn kind(_: &<#resource as #kube_core::Resource>::DynamicType) -> std::borrow::Cow<'_, str> {
                #resource::kind(&Default::default()).into_owned().into()
            }

            fn version(_: &<#resource as #kube_core::Resource>::DynamicType) -> std::borrow::Cow<'_, str> {
                #resource::version(&Default::default()).into_owned().into()
            }

            fn api_version(_: &<#resource as #kube_core::Resource>::DynamicType) -> std::borrow::Cow<'_, str> {
                #resource::api_version(&Default::default()).into_owned().into()
            }

            fn plural(_: &<#resource as #kube_core::Resource>::DynamicType) -> std::borrow::Cow<'_, str> {
                #resource::plural(&Default::default()).into_owned().into()
            }

            fn meta(&self) -> &#k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta {
                &self.metadata
            }

            fn meta_mut(&mut self) -> &mut #k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta {
                &mut self.metadata
            }
        }
    };

    // Concat output
    quote! {
        #inherit_resource
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_inherit() {
        let input = quote! {
            #[derive(Resource)]
            #[resource(inherit = "ConfigMap")]
            struct Foo { metadata: ObjectMeta }
        };

        let input = syn::parse2(input).unwrap();
        InheritAttrs::from_derive_input(&input).unwrap();
    }
}