diff --git a/crates/stackable-versioned-macros/src/codegen/container/enum.rs b/crates/stackable-versioned-macros/src/codegen/container/enum.rs index 4f53b149c..0b254cde4 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/enum.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/enum.rs @@ -74,9 +74,12 @@ pub struct Enum { /// and where clause. /// /// We use a [`OnceCell`] here to avoid outer mutability and instead rely on interior mutability - /// to lazily initialize this field. This is done to avoid the macro the re-clone and + /// to lazily initialize this field. This is done to avoid the macro re-cloning and /// re-construct these generics over and over again. /// + /// NOTE: Do **NOT** make public. Access to this field and its value is handled in a controlled + /// fashion via [`Enum::get_tracking_generics`]. + /// /// SAFETY: Using [`OnceCell`] instead of `OnceLock` is fine here, because proc-macros run in a /// single thread. tracking_generics: OnceCell, @@ -205,10 +208,10 @@ impl Enum { quote! { #automatically_derived #allow_attribute - impl #impl_generics #versioned_path::TrackingFrom<#from_module_ident::#enum_ident #type_generics, S> for #for_module_ident::#enum_ident #type_generics + impl #impl_generics #versioned_path::TrackingFrom<#from_module_ident::#enum_ident #type_generics, __SvStatus> for #for_module_ident::#enum_ident #type_generics #where_clause { - fn tracking_from(#from_enum_ident: #from_module_ident::#enum_ident #type_generics, status: &mut S, parent: &str) -> Self { + fn tracking_from(#from_enum_ident: #from_module_ident::#enum_ident #type_generics, status: &mut __SvStatus, parent: &str) -> Self { // TODO (@Techassi): Only emit this if any of the variants below need it use #versioned_path::TrackingInto as _; @@ -247,11 +250,16 @@ impl Enum { self.tracking_generics.get_or_init(|| { let versioned_path = &*mod_gen_ctx.crates.versioned; + // The generic type is prefixed to avoid collisions with generic types declared on the + // enum, like `enum Foo`. let mut tracking_generics = self.generics.clone(); - tracking_generics.params.push(parse_quote! { S }); - tracking_generics.make_where_clause().predicates.push( - parse_quote! { S: #versioned_path::TrackingStatus + ::core::default::Default }, - ); + tracking_generics.params.push(parse_quote! { __SvStatus }); + tracking_generics + .make_where_clause() + .predicates + .push(parse_quote! { + __SvStatus: #versioned_path::TrackingStatus + ::core::default::Default + }); tracking_generics }) diff --git a/crates/stackable-versioned-macros/src/codegen/container/struct/conversion.rs b/crates/stackable-versioned-macros/src/codegen/container/struct/conversion.rs index 9c838ef65..419e239fe 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/struct/conversion.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/struct/conversion.rs @@ -1,10 +1,12 @@ +#[cfg(doc)] +use std::cell::OnceCell; use std::{borrow::Cow, cmp::Ordering}; use indoc::formatdoc; use itertools::Itertools as _; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use syn::parse_quote; +use syn::{Generics, parse_quote}; use crate::{ codegen::{ @@ -122,6 +124,10 @@ impl Struct { // defined struct always represents the latest version. I guess we could generally // advise against using generic types, but if you have to, avoid removing it in // later versions. + let (_, type_generics, _) = self.generics.split_for_impl(); + let (impl_generics, _, where_clause) = + self.get_tracking_generics(mod_gen_ctx).split_for_impl(); + let from_struct_ident = &self.common.idents.parameter; let struct_ident = &self.common.idents.original; @@ -173,17 +179,15 @@ impl Struct { let removals = self.generate_tracking_removals(direction, next_version, mod_gen_ctx); let json_paths = self.generate_json_paths(next_version, mod_gen_ctx); - // TODO (@Techassi): Re-add support for generics // TODO (@Techassi): We know the status, so we can hard-code it, but hard to track across structs quote! { #automatically_derived #allow_attribute - impl #versioned_path::TrackingFrom<#from_module_ident::#struct_ident, S> for #for_module_ident::#struct_ident - where - S: #versioned_path::TrackingStatus + ::core::default::Default + impl #impl_generics #versioned_path::TrackingFrom<#from_module_ident::#struct_ident #type_generics, __SvStatus> for #for_module_ident::#struct_ident #type_generics + #where_clause { - fn tracking_from(#from_struct_ident: #from_module_ident::#struct_ident, status: &mut S, parent: &str) -> Self { + fn tracking_from(#from_struct_ident: #from_module_ident::#struct_ident #type_generics, status: &mut __SvStatus, parent: &str) -> Self { // TODO (@Techassi): Only emit this if any of the fields below need it use #versioned_path::TrackingInto as _; @@ -809,6 +813,32 @@ impl Struct { TracingTokens::default() } } + + /// Construct (if needed) and return the [`Struct`]'s generics extended with the tracking status + /// generic type and where clause. + /// + /// This function lazily initializes the underlying field in [`Struct`]. Afterwards, the value + /// is re-used without constructing the value from scratch over and over again. This is achieved + /// through interior mutability via [`OnceCell`]. See [`Struct`]'s `tracking_generics` field for + /// more details. + fn get_tracking_generics(&self, mod_gen_ctx: ModuleGenerationContext<'_>) -> &Generics { + self.tracking_generics.get_or_init(|| { + let versioned_path = &*mod_gen_ctx.crates.versioned; + + // The generic type is prefixed to avoid collisions with generic types declared on the + // struct, like `struct Foo`. + let mut tracking_generics = self.generics.clone(); + tracking_generics.params.push(parse_quote! { __SvStatus }); + tracking_generics + .make_where_clause() + .predicates + .push(parse_quote! { + __SvStatus: #versioned_path::TrackingStatus + ::core::default::Default + }); + + tracking_generics + }) + } } fn conversion_paths(elements: &[T]) -> Vec<(&T, Cow<'_, [T]>)> diff --git a/crates/stackable-versioned-macros/src/codegen/container/struct/mod.rs b/crates/stackable-versioned-macros/src/codegen/container/struct/mod.rs index 922e2d3f6..4df659dfc 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/struct/mod.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/struct/mod.rs @@ -1,3 +1,5 @@ +use std::cell::OnceCell; + use darling::{Error, FromAttributes, Result, util::IdentString}; use itertools::Itertools; use proc_macro2::TokenStream; @@ -78,6 +80,7 @@ impl Container { }; Ok(Self::Struct(Struct { + tracking_generics: OnceCell::new(), generics: item_struct.generics, kubernetes_data, fields: versioned_fields, @@ -100,6 +103,20 @@ pub struct Struct { /// Generic types of the struct pub generics: Generics, + + /// Generic types of the struct which additionally also include the tracking state generic type + /// and where clause. + /// + /// We use a [`OnceCell`] here to avoid outer mutability and instead rely on interior mutability + /// to lazily initialize this field. This is done to avoid the macro re-cloning and + /// re-construct these generics over and over again. + /// + /// NOTE: Do **NOT** make public. Access to this field and its value is handled in a controlled + /// fashion via [`Struct::get_tracking_generics`]. + /// + /// SAFETY: Using [`OnceCell`] instead of `OnceLock` is fine here, because proc-macros run in a + /// single thread. + tracking_generics: OnceCell, } #[derive(Debug)] diff --git a/crates/stackable-versioned-macros/tests/inputs/pass/conversion_tracking.rs b/crates/stackable-versioned-macros/tests/inputs/pass/conversion_tracking.rs index 68db56728..dd3ecd729 100644 --- a/crates/stackable-versioned-macros/tests/inputs/pass/conversion_tracking.rs +++ b/crates/stackable-versioned-macros/tests/inputs/pass/conversion_tracking.rs @@ -30,7 +30,3 @@ fn main() {} pub struct FooStatus { foo: String, } - -fn usize_to_u16(input: usize) -> u16 { - input.try_into().unwrap() -} diff --git a/crates/stackable-versioned-macros/tests/inputs/pass/conversion_tracking_generics.rs b/crates/stackable-versioned-macros/tests/inputs/pass/conversion_tracking_generics.rs new file mode 100644 index 000000000..33bd3091e --- /dev/null +++ b/crates/stackable-versioned-macros/tests/inputs/pass/conversion_tracking_generics.rs @@ -0,0 +1,57 @@ +use kube::CustomResource; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use stackable_versioned::versioned; +// --- +#[versioned( + version(name = "v1alpha1"), + version(name = "v1beta1"), + version(name = "v1"), + options(k8s(experimental_conversion_tracking)) +)] +// --- +pub(crate) mod versioned { + #[versioned(crd( + group = "stackable.tech", + status = FooStatus, + doc = "Test", + ))] + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)] + pub(crate) struct FooSpec { + #[versioned(added(since = "v1beta1"), changed(since = "v1", from_name = "bah"))] + bar: usize, + + #[versioned(nested)] + baz: Baz, + } + + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] + #[allow(dead_code)] + // The generic type is named S on purpose, to ensure it doesn't collide with the generic type + // used for the tracking status in the generated TrackingFrom impls. + pub(crate) struct Baz + where + S: Clone + Default, + { + inner_1: bool, + + // FIXME (@Techassi): This currently leads to errors, because the generic type S is emitted + // for v1alpha1 as well. There it is unused. We need to somehow figure out if a generic type + // is used and if so, emit it. Otherwise, don't emit it. + // #[versioned(added(since = "v1beta1"))] + inner_2: S, + + // Concrete type on purpose, see the FIXME above. Added in v1 (not v1beta1) so it doesn't + // share the v1beta1 upgrade bucket with FooSpec::bar. + // FIXME: This is a known BUG and will be fixed in the future. + #[versioned(added(since = "v1"))] + inner_3: String, + } +} +// --- +fn main() {} + +#[derive(Clone, Debug, Default, JsonSchema, Deserialize, Serialize)] +pub struct FooStatus { + foo: String, +} diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking.rs.snap index bfd6a7486..54b85a93d 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking.rs.snap @@ -49,13 +49,14 @@ impl ::core::convert::From for v1alpha1::Foo { } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1beta1::FooSpec +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1beta1::FooSpec where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { fn tracking_from( __sv_foospec: v1alpha1::FooSpec, - status: &mut S, + status: &mut __SvStatus, parent: &str, ) -> Self { use ::stackable_versioned::TrackingInto as _; @@ -78,13 +79,14 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1alpha1::FooSpec +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1alpha1::FooSpec where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { fn tracking_from( __sv_foospec: v1beta1::FooSpec, - status: &mut S, + status: &mut __SvStatus, parent: &str, ) -> Self { use ::stackable_versioned::TrackingInto as _; @@ -152,13 +154,14 @@ impl ::core::convert::From for v1beta1::Foo { } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1::FooSpec +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1::FooSpec where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { fn tracking_from( __sv_foospec: v1beta1::FooSpec, - status: &mut S, + status: &mut __SvStatus, parent: &str, ) -> Self { use ::stackable_versioned::TrackingInto as _; @@ -170,11 +173,16 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1beta1::FooSpec +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1beta1::FooSpec where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_foospec: v1::FooSpec, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_foospec: v1::FooSpec, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; let mut spec = Self { bah: __sv_foospec.bar.into(), diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_enum.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_enum.rs.snap index 22bab38fa..42addbd12 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_enum.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_enum.rs.snap @@ -62,13 +62,14 @@ impl ::core::convert::From for v1alpha1::Foo { } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1alpha2::FooSpec +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1alpha2::FooSpec where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { fn tracking_from( __sv_foospec: v1alpha1::FooSpec, - status: &mut S, + status: &mut __SvStatus, parent: &str, ) -> Self { use ::stackable_versioned::TrackingInto as _; @@ -83,13 +84,14 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1alpha1::FooSpec +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1alpha1::FooSpec where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { fn tracking_from( __sv_foospec: v1alpha2::FooSpec, - status: &mut S, + status: &mut __SvStatus, parent: &str, ) -> Self { use ::stackable_versioned::TrackingInto as _; @@ -104,14 +106,14 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom +impl<__SvStatus> ::stackable_versioned::TrackingFrom for v1alpha2::Connector where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { fn tracking_from( __sv_connector: v1alpha1::Connector, - status: &mut S, + status: &mut __SvStatus, parent: &str, ) -> Self { use ::stackable_versioned::TrackingInto as _; @@ -157,14 +159,14 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom +impl<__SvStatus> ::stackable_versioned::TrackingFrom for v1alpha1::Connector where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { fn tracking_from( __sv_connector: v1alpha2::Connector, - status: &mut S, + status: &mut __SvStatus, parent: &str, ) -> Self { use ::stackable_versioned::TrackingInto as _; @@ -210,14 +212,16 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom +impl< + __SvStatus, +> ::stackable_versioned::TrackingFrom for v1alpha2::IcebergConnector where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { fn tracking_from( __sv_icebergconnector: v1alpha1::IcebergConnector, - status: &mut S, + status: &mut __SvStatus, parent: &str, ) -> Self { use ::stackable_versioned::TrackingInto as _; @@ -244,14 +248,16 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom +impl< + __SvStatus, +> ::stackable_versioned::TrackingFrom for v1alpha1::IcebergConnector where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { fn tracking_from( __sv_icebergconnector: v1alpha2::IcebergConnector, - status: &mut S, + status: &mut __SvStatus, parent: &str, ) -> Self { use ::stackable_versioned::TrackingInto as _; diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_generics.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_generics.rs.snap new file mode 100644 index 000000000..d20e46bbc --- /dev/null +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_generics.rs.snap @@ -0,0 +1,657 @@ +--- +source: crates/stackable-versioned-macros/src/lib.rs +expression: formatted +input_file: crates/stackable-versioned-macros/tests/inputs/pass/conversion_tracking_generics.rs +--- +#[automatically_derived] +pub(crate) mod v1alpha1 { + use super::*; + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)] + #[kube( + group = "stackable.tech", + version = "v1alpha1", + kind = "Foo", + doc = "Test", + status = FooStatusWithChangedValues + )] + pub struct FooSpec { + pub baz: Baz, + } + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] + #[allow(dead_code)] + pub struct Baz + where + S: Clone + Default, + { + pub inner_1: bool, + pub inner_2: S, + } +} +#[automatically_derived] +impl ::core::convert::From for v1beta1::Foo { + fn from(__sv_foo: v1alpha1::Foo) -> Self { + let mut status = __sv_foo.status.unwrap_or_default(); + let spec = >::tracking_from(__sv_foo.spec, &mut status, "$"); + Self { + metadata: __sv_foo.metadata, + status: Some(status), + spec, + } + } +} +#[automatically_derived] +impl ::core::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + let mut status = __sv_foo.status.unwrap_or_default(); + let spec = >::tracking_from(__sv_foo.spec, &mut status, "$"); + Self { + metadata: __sv_foo.metadata, + status: Some(status), + spec, + } + } +} +#[automatically_derived] +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1beta1::FooSpec +where + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, +{ + fn tracking_from( + __sv_foospec: v1alpha1::FooSpec, + status: &mut __SvStatus, + parent: &str, + ) -> Self { + use ::stackable_versioned::TrackingInto as _; + let __sv_bah_path = ::stackable_versioned::jthong_path(parent, "bah"); + let __sv_baz_path = ::stackable_versioned::jthong_path(parent, "baz"); + let mut spec = Self { + bah: ::std::default::Default::default(), + baz: __sv_foospec.baz.tracking_into(status, &__sv_baz_path), + }; + if let Some(upgrades) = status.changes().upgrades.remove(&"v1beta1".to_owned()) { + for ::stackable_versioned::ChangedValue { json_path, value } in upgrades { + match json_path { + json_path if json_path == __sv_bah_path => { + spec.bah = ::yaml_serde::from_value(value).unwrap(); + } + _ => unreachable!(), + } + } + } + spec + } +} +#[automatically_derived] +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1alpha1::FooSpec +where + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, +{ + fn tracking_from( + __sv_foospec: v1beta1::FooSpec, + status: &mut __SvStatus, + parent: &str, + ) -> Self { + use ::stackable_versioned::TrackingInto as _; + let __sv_bah_path = ::stackable_versioned::jthong_path(parent, "bah"); + let __sv_baz_path = ::stackable_versioned::jthong_path(parent, "baz"); + let upgrades = status + .changes() + .upgrades + .entry("v1beta1".to_owned()) + .or_default(); + upgrades + .push(::stackable_versioned::ChangedValue { + json_path: __sv_bah_path, + value: ::yaml_serde::to_value(&__sv_foospec.bah).unwrap(), + }); + let mut spec = Self { + baz: __sv_foospec.baz.tracking_into(status, &__sv_baz_path), + }; + spec + } +} +#[automatically_derived] +impl ::stackable_versioned::TrackingFrom, __SvStatus> +for v1beta1::Baz +where + S: Clone + Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, +{ + fn tracking_from( + __sv_baz: v1alpha1::Baz, + status: &mut __SvStatus, + parent: &str, + ) -> Self { + use ::stackable_versioned::TrackingInto as _; + let mut spec = Self { + inner_1: __sv_baz.inner_1.into(), + inner_2: __sv_baz.inner_2.into(), + }; + spec + } +} +#[automatically_derived] +impl ::stackable_versioned::TrackingFrom, __SvStatus> +for v1alpha1::Baz +where + S: Clone + Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, +{ + fn tracking_from( + __sv_baz: v1beta1::Baz, + status: &mut __SvStatus, + parent: &str, + ) -> Self { + use ::stackable_versioned::TrackingInto as _; + let mut spec = Self { + inner_1: __sv_baz.inner_1.into(), + inner_2: __sv_baz.inner_2.into(), + }; + spec + } +} +#[automatically_derived] +pub(crate) mod v1beta1 { + use super::*; + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)] + #[kube( + group = "stackable.tech", + version = "v1beta1", + kind = "Foo", + doc = "Test", + status = FooStatusWithChangedValues + )] + pub struct FooSpec { + pub bah: usize, + pub baz: Baz, + } + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] + #[allow(dead_code)] + pub struct Baz + where + S: Clone + Default, + { + pub inner_1: bool, + pub inner_2: S, + } +} +#[automatically_derived] +impl ::core::convert::From for v1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + let mut status = __sv_foo.status.unwrap_or_default(); + let spec = >::tracking_from(__sv_foo.spec, &mut status, "$"); + Self { + metadata: __sv_foo.metadata, + status: Some(status), + spec, + } + } +} +#[automatically_derived] +impl ::core::convert::From for v1beta1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + let mut status = __sv_foo.status.unwrap_or_default(); + let spec = >::tracking_from(__sv_foo.spec, &mut status, "$"); + Self { + metadata: __sv_foo.metadata, + status: Some(status), + spec, + } + } +} +#[automatically_derived] +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1::FooSpec +where + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, +{ + fn tracking_from( + __sv_foospec: v1beta1::FooSpec, + status: &mut __SvStatus, + parent: &str, + ) -> Self { + use ::stackable_versioned::TrackingInto as _; + let __sv_baz_path = ::stackable_versioned::jthong_path(parent, "baz"); + let mut spec = Self { + bar: __sv_foospec.bah.into(), + baz: __sv_foospec.baz.tracking_into(status, &__sv_baz_path), + }; + spec + } +} +#[automatically_derived] +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1beta1::FooSpec +where + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, +{ + fn tracking_from( + __sv_foospec: v1::FooSpec, + status: &mut __SvStatus, + parent: &str, + ) -> Self { + use ::stackable_versioned::TrackingInto as _; + let __sv_baz_path = ::stackable_versioned::jthong_path(parent, "baz"); + let mut spec = Self { + bah: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.tracking_into(status, &__sv_baz_path), + }; + spec + } +} +#[automatically_derived] +impl ::stackable_versioned::TrackingFrom, __SvStatus> +for v1::Baz +where + S: Clone + Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, +{ + fn tracking_from( + __sv_baz: v1beta1::Baz, + status: &mut __SvStatus, + parent: &str, + ) -> Self { + use ::stackable_versioned::TrackingInto as _; + let __sv_inner_3_path = ::stackable_versioned::jthong_path(parent, "inner_3"); + let mut spec = Self { + inner_1: __sv_baz.inner_1.into(), + inner_2: __sv_baz.inner_2.into(), + inner_3: ::std::default::Default::default(), + }; + if let Some(upgrades) = status.changes().upgrades.remove(&"v1".to_owned()) { + for ::stackable_versioned::ChangedValue { json_path, value } in upgrades { + match json_path { + json_path if json_path == __sv_inner_3_path => { + spec.inner_3 = ::yaml_serde::from_value(value).unwrap(); + } + _ => unreachable!(), + } + } + } + spec + } +} +#[automatically_derived] +impl ::stackable_versioned::TrackingFrom, __SvStatus> +for v1beta1::Baz +where + S: Clone + Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, +{ + fn tracking_from( + __sv_baz: v1::Baz, + status: &mut __SvStatus, + parent: &str, + ) -> Self { + use ::stackable_versioned::TrackingInto as _; + let __sv_inner_3_path = ::stackable_versioned::jthong_path(parent, "inner_3"); + let upgrades = status.changes().upgrades.entry("v1".to_owned()).or_default(); + upgrades + .push(::stackable_versioned::ChangedValue { + json_path: __sv_inner_3_path, + value: ::yaml_serde::to_value(&__sv_baz.inner_3).unwrap(), + }); + let mut spec = Self { + inner_1: __sv_baz.inner_1.into(), + inner_2: __sv_baz.inner_2.into(), + }; + spec + } +} +#[automatically_derived] +pub(crate) mod v1 { + use super::*; + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)] + #[kube( + group = "stackable.tech", + version = "v1", + kind = "Foo", + doc = "Test", + status = FooStatusWithChangedValues + )] + pub struct FooSpec { + pub bar: usize, + pub baz: Baz, + } + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] + #[allow(dead_code)] + pub struct Baz + where + S: Clone + Default, + { + pub inner_1: bool, + pub inner_2: S, + pub inner_3: String, + } +} +#[automatically_derived] +#[derive(::core::fmt::Debug)] +pub(crate) enum Foo { + V1Alpha1(v1alpha1::Foo), + V1Beta1(v1beta1::Foo), + V1(v1::Foo), +} +#[automatically_derived] +impl Foo { + /// Generates a merged CRD containing all versions and marking `stored_apiversion` as stored. + pub fn merged_crd( + stored_apiversion: FooVersion, + ) -> ::std::result::Result< + ::k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition, + ::kube::core::crd::MergeError, + > { + ::kube::core::crd::merge_crds( + vec![ + < v1alpha1::Foo as ::kube::core::CustomResourceExt > ::crd(), < + v1beta1::Foo as ::kube::core::CustomResourceExt > ::crd(), < v1::Foo as + ::kube::core::CustomResourceExt > ::crd() + ], + stored_apiversion.as_version_str(), + ) + } + /// Tries to convert a list of objects of kind [`Foo`] to the desired API version + /// specified in the [`ConversionReview`][cr]. + /// + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. + /// + /// [cr]: ::kube::core::conversion::ConversionReview + pub fn try_convert( + review: ::kube::core::conversion::ConversionReview, + ) -> ::kube::core::conversion::ConversionReview { + let request = match ::kube::core::conversion::ConversionRequest::from_review( + review, + ) { + ::std::result::Result::Ok(request) => request, + ::std::result::Result::Err(err) => { + return ::kube::core::conversion::ConversionResponse::invalid(::kube::core::Status { + status: Some(::kube::core::response::StatusSummary::Failure), + message: err.to_string(), + metadata: None, + reason: err.to_string(), + details: None, + code: 400, + }) + .into_review(); + } + }; + let response = match Self::convert_objects( + request.objects, + &request.desired_api_version, + ) { + ::std::result::Result::Ok(converted_objects) => { + ::kube::core::conversion::ConversionResponse { + result: ::kube::core::Status::success(), + types: request.types, + uid: request.uid, + converted_objects, + } + } + ::std::result::Result::Err(err) => { + let code = err.http_status_code(); + let message = err.join_errors(); + ::kube::core::conversion::ConversionResponse { + result: ::kube::core::Status { + status: Some(::kube::core::response::StatusSummary::Failure), + message: message.clone(), + metadata: None, + reason: message, + details: None, + code, + }, + types: request.types, + uid: request.uid, + converted_objects: vec![], + } + } + }; + response.into_review() + } + fn convert_objects( + objects: ::std::vec::Vec<::serde_json::Value>, + desired_api_version: &str, + ) -> ::std::result::Result< + ::std::vec::Vec<::serde_json::Value>, + ::stackable_versioned::ConvertObjectError, + > { + let desired_api_version = FooVersion::from_api_version(desired_api_version) + .map_err(|source| ::stackable_versioned::ConvertObjectError::ParseDesiredApiVersion { + source, + })?; + let mut converted_objects = ::std::vec::Vec::with_capacity(objects.len()); + for object in objects { + let current_object = Self::from_json_object(object.clone()) + .map_err(|source| ::stackable_versioned::ConvertObjectError::Parse { + source, + })?; + match (current_object, desired_api_version) { + (Self::V1Alpha1(__sv_foo), FooVersion::V1Beta1) => { + let converted: v1beta1::Foo = __sv_foo.into(); + let desired_object = Self::V1Beta1(converted); + let desired_object = desired_object + .into_json_value() + .map_err(|source| ::stackable_versioned::ConvertObjectError::Serialize { + source, + })?; + converted_objects.push(desired_object); + } + (Self::V1Alpha1(__sv_foo), FooVersion::V1) => { + let converted: v1beta1::Foo = __sv_foo.into(); + let converted: v1::Foo = converted.into(); + let desired_object = Self::V1(converted); + let desired_object = desired_object + .into_json_value() + .map_err(|source| ::stackable_versioned::ConvertObjectError::Serialize { + source, + })?; + converted_objects.push(desired_object); + } + (Self::V1Beta1(__sv_foo), FooVersion::V1Alpha1) => { + let converted: v1alpha1::Foo = __sv_foo.into(); + let desired_object = Self::V1Alpha1(converted); + let desired_object = desired_object + .into_json_value() + .map_err(|source| ::stackable_versioned::ConvertObjectError::Serialize { + source, + })?; + converted_objects.push(desired_object); + } + (Self::V1Beta1(__sv_foo), FooVersion::V1) => { + let converted: v1::Foo = __sv_foo.into(); + let desired_object = Self::V1(converted); + let desired_object = desired_object + .into_json_value() + .map_err(|source| ::stackable_versioned::ConvertObjectError::Serialize { + source, + })?; + converted_objects.push(desired_object); + } + (Self::V1(__sv_foo), FooVersion::V1Alpha1) => { + let converted: v1beta1::Foo = __sv_foo.into(); + let converted: v1alpha1::Foo = converted.into(); + let desired_object = Self::V1Alpha1(converted); + let desired_object = desired_object + .into_json_value() + .map_err(|source| ::stackable_versioned::ConvertObjectError::Serialize { + source, + })?; + converted_objects.push(desired_object); + } + (Self::V1(__sv_foo), FooVersion::V1Beta1) => { + let converted: v1beta1::Foo = __sv_foo.into(); + let desired_object = Self::V1Beta1(converted); + let desired_object = desired_object + .into_json_value() + .map_err(|source| ::stackable_versioned::ConvertObjectError::Serialize { + source, + })?; + converted_objects.push(desired_object); + } + _ => converted_objects.push(object), + } + } + ::std::result::Result::Ok(converted_objects) + } + fn from_json_object( + object_value: ::serde_json::Value, + ) -> ::std::result::Result { + let kind = object_value + .get("kind") + .ok_or_else(|| ::stackable_versioned::ParseObjectError::FieldNotPresent { + field: "kind".to_owned(), + })? + .as_str() + .ok_or_else(|| ::stackable_versioned::ParseObjectError::FieldNotStr { + field: "kind".to_owned(), + })?; + if kind != "Foo" { + return Err(::stackable_versioned::ParseObjectError::UnexpectedKind { + kind: kind.to_owned(), + expected: "Foo".to_owned(), + }); + } + let api_version = object_value + .get("apiVersion") + .ok_or_else(|| ::stackable_versioned::ParseObjectError::FieldNotPresent { + field: "apiVersion".to_owned(), + })? + .as_str() + .ok_or_else(|| ::stackable_versioned::ParseObjectError::FieldNotStr { + field: "apiVersion".to_owned(), + })?; + let object = match api_version { + "stackable.tech/v1alpha1" => { + let object = ::serde_json::from_value(object_value) + .map_err(|source| ::stackable_versioned::ParseObjectError::Deserialize { + source, + })?; + Self::V1Alpha1(object) + } + "stackable.tech/v1beta1" => { + let object = ::serde_json::from_value(object_value) + .map_err(|source| ::stackable_versioned::ParseObjectError::Deserialize { + source, + })?; + Self::V1Beta1(object) + } + "stackable.tech/v1" => { + let object = ::serde_json::from_value(object_value) + .map_err(|source| ::stackable_versioned::ParseObjectError::Deserialize { + source, + })?; + Self::V1(object) + } + unknown_api_version => { + return ::std::result::Result::Err(::stackable_versioned::ParseObjectError::UnknownApiVersion { + api_version: unknown_api_version.to_owned(), + }); + } + }; + ::std::result::Result::Ok(object) + } + fn into_json_value( + self, + ) -> ::std::result::Result<::serde_json::Value, ::serde_json::Error> { + match self { + Self::V1Alpha1(__sv_foo) => Ok(::serde_json::to_value(__sv_foo)?), + Self::V1Beta1(__sv_foo) => Ok(::serde_json::to_value(__sv_foo)?), + Self::V1(__sv_foo) => Ok(::serde_json::to_value(__sv_foo)?), + } + } +} +#[automatically_derived] +#[derive(::core::marker::Copy, ::core::clone::Clone, ::core::fmt::Debug)] +pub(crate) enum FooVersion { + V1Alpha1, + V1Beta1, + V1, +} +#[automatically_derived] +impl ::core::fmt::Display for FooVersion { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::std::result::Result<(), ::std::fmt::Error> { + f.write_str(self.as_version_str()) + } +} +#[automatically_derived] +impl FooVersion { + pub fn as_version_str(&self) -> &str { + match self { + FooVersion::V1Alpha1 => "v1alpha1", + FooVersion::V1Beta1 => "v1beta1", + FooVersion::V1 => "v1", + } + } + pub fn as_api_version_str(&self) -> &str { + match self { + FooVersion::V1Alpha1 => "stackable.tech/v1alpha1", + FooVersion::V1Beta1 => "stackable.tech/v1beta1", + FooVersion::V1 => "stackable.tech/v1", + } + } + pub fn from_api_version( + api_version: &str, + ) -> Result { + match api_version { + "stackable.tech/v1alpha1" => Ok(FooVersion::V1Alpha1), + "stackable.tech/v1beta1" => Ok(FooVersion::V1Beta1), + "stackable.tech/v1" => Ok(FooVersion::V1), + _ => { + Err(::stackable_versioned::UnknownDesiredApiVersionError { + api_version: api_version.to_owned(), + }) + } + } + } +} +#[cfg(test)] +#[test] +fn FooSpec_roundtrip_down_up() { + ::stackable_versioned::test_utils::test_roundtrip::< + v1::FooSpec, + >(stringify!(Foo), "stackable.tech/v1", "stackable.tech/v1alpha1", Foo::try_convert); +} +#[cfg(test)] +#[test] +fn FooSpec_roundtrip_up_down() { + ::stackable_versioned::test_utils::test_roundtrip::< + v1alpha1::FooSpec, + >(stringify!(Foo), "stackable.tech/v1alpha1", "stackable.tech/v1", Foo::try_convert); +} +#[automatically_derived] +#[derive( + ::core::clone::Clone, + ::core::default::Default, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct FooStatusWithChangedValues { + pub changed_values: ::stackable_versioned::ChangedValues, + #[serde(flatten)] + pub status: FooStatus, +} +#[automatically_derived] +impl ::stackable_versioned::TrackingStatus for FooStatusWithChangedValues { + fn changes(&mut self) -> &mut ::stackable_versioned::ChangedValues { + &mut self.changed_values + } +} diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_hints.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_hints.rs.snap index 3dbe2f76c..b45919ea7 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_hints.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_hints.rs.snap @@ -16,11 +16,16 @@ mod v1alpha1 { } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1alpha2::Foo +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1alpha2::Foo where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_foo: v1alpha1::Foo, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_foo: v1alpha1::Foo, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; let __sv_bar_path = ::stackable_versioned::jthong_path(parent, "bar"); let mut spec = Self { @@ -32,11 +37,16 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1alpha1::Foo +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1alpha1::Foo where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_foo: v1alpha2::Foo, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_foo: v1alpha2::Foo, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; let __sv_bar_path = ::stackable_versioned::jthong_path(parent, "bar"); let mut spec = Self { @@ -48,11 +58,16 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1alpha2::Bar +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1alpha2::Bar where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_bar: v1alpha1::Bar, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_bar: v1alpha1::Bar, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; let __sv_baz_baz_path = ::stackable_versioned::jthong_path(parent, "baz_baz"); let mut spec = Self { @@ -74,11 +89,16 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1alpha1::Bar +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1alpha1::Bar where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_bar: v1alpha2::Bar, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_bar: v1alpha2::Bar, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; let __sv_baz_baz_path = ::stackable_versioned::jthong_path(parent, "baz_baz"); let upgrades = status diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@docs.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@docs.rs.snap index 287f78240..fd4d643bb 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@docs.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@docs.rs.snap @@ -32,11 +32,16 @@ mod v1alpha1 { } #[automatically_derived] #[allow(deprecated)] -impl ::stackable_versioned::TrackingFrom for v1beta1::Foo +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1beta1::Foo where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_foo: v1alpha1::Foo, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_foo: v1alpha1::Foo, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; let __sv_baz_path = ::stackable_versioned::jthong_path(parent, "baz"); let mut spec = Self { @@ -62,11 +67,16 @@ where } #[automatically_derived] #[allow(deprecated)] -impl ::stackable_versioned::TrackingFrom for v1alpha1::Foo +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1alpha1::Foo where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_foo: v1beta1::Foo, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_foo: v1beta1::Foo, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; let __sv_baz_path = ::stackable_versioned::jthong_path(parent, "baz"); let upgrades = status @@ -90,11 +100,16 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1beta1::Bar +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1beta1::Bar where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_bar: v1alpha1::Bar, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_bar: v1alpha1::Bar, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; match __sv_bar { v1alpha1::Bar::Baz => v1beta1::Bar::Baz, @@ -102,11 +117,16 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1alpha1::Bar +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1alpha1::Bar where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_bar: v1beta1::Bar, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_bar: v1beta1::Bar, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; match __sv_bar { v1beta1::Bar::Baz => v1alpha1::Bar::Baz, @@ -153,11 +173,16 @@ mod v1beta1 { } #[automatically_derived] #[allow(deprecated)] -impl ::stackable_versioned::TrackingFrom for v1beta2::Foo +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1beta2::Foo where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_foo: v1beta1::Foo, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_foo: v1beta1::Foo, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; let mut spec = Self { foo: __sv_foo.foo.into(), @@ -172,11 +197,16 @@ where } #[automatically_derived] #[allow(deprecated)] -impl ::stackable_versioned::TrackingFrom for v1beta1::Foo +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1beta1::Foo where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_foo: v1beta2::Foo, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_foo: v1beta2::Foo, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; let mut spec = Self { foo: __sv_foo.foo.into(), @@ -190,11 +220,16 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1beta2::Bar +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1beta2::Bar where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_bar: v1beta1::Bar, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_bar: v1beta1::Bar, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; match __sv_bar { v1beta1::Bar::Baz => v1beta2::Bar::Baz, @@ -202,11 +237,16 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1beta1::Bar +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1beta1::Bar where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_bar: v1beta2::Bar, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_bar: v1beta2::Bar, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; match __sv_bar { v1beta2::Bar::Baz => v1beta1::Bar::Baz, @@ -247,11 +287,16 @@ mod v1beta2 { } #[automatically_derived] #[allow(deprecated)] -impl ::stackable_versioned::TrackingFrom for v1::Foo +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1::Foo where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_foo: v1beta2::Foo, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_foo: v1beta2::Foo, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; let mut spec = Self { foo: __sv_foo.foo.into(), @@ -266,11 +311,12 @@ where } #[automatically_derived] #[allow(deprecated)] -impl ::stackable_versioned::TrackingFrom for v1beta2::Foo +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1beta2::Foo where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_foo: v1::Foo, status: &mut S, parent: &str) -> Self { + fn tracking_from(__sv_foo: v1::Foo, status: &mut __SvStatus, parent: &str) -> Self { use ::stackable_versioned::TrackingInto as _; let mut spec = Self { foo: __sv_foo.foo.into(), @@ -284,11 +330,16 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1::Bar +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1::Bar where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_bar: v1beta2::Bar, status: &mut S, parent: &str) -> Self { + fn tracking_from( + __sv_bar: v1beta2::Bar, + status: &mut __SvStatus, + parent: &str, + ) -> Self { use ::stackable_versioned::TrackingInto as _; match __sv_bar { v1beta2::Bar::Baz => v1::Bar::Baz, @@ -296,11 +347,12 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1beta2::Bar +impl<__SvStatus> ::stackable_versioned::TrackingFrom +for v1beta2::Bar where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_bar: v1::Bar, status: &mut S, parent: &str) -> Self { + fn tracking_from(__sv_bar: v1::Bar, status: &mut __SvStatus, parent: &str) -> Self { use ::stackable_versioned::TrackingInto as _; match __sv_bar { v1::Bar::Baz => v1beta2::Bar::Baz, @@ -341,11 +393,11 @@ mod v1 { } #[automatically_derived] #[allow(deprecated)] -impl ::stackable_versioned::TrackingFrom for v2::Foo +impl<__SvStatus> ::stackable_versioned::TrackingFrom for v2::Foo where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_foo: v1::Foo, status: &mut S, parent: &str) -> Self { + fn tracking_from(__sv_foo: v1::Foo, status: &mut __SvStatus, parent: &str) -> Self { use ::stackable_versioned::TrackingInto as _; let mut spec = Self { foo: __sv_foo.foo.into(), @@ -360,11 +412,11 @@ where } #[automatically_derived] #[allow(deprecated)] -impl ::stackable_versioned::TrackingFrom for v1::Foo +impl<__SvStatus> ::stackable_versioned::TrackingFrom for v1::Foo where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_foo: v2::Foo, status: &mut S, parent: &str) -> Self { + fn tracking_from(__sv_foo: v2::Foo, status: &mut __SvStatus, parent: &str) -> Self { use ::stackable_versioned::TrackingInto as _; let mut spec = Self { foo: __sv_foo.foo.into(), @@ -378,11 +430,11 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v2::Bar +impl<__SvStatus> ::stackable_versioned::TrackingFrom for v2::Bar where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_bar: v1::Bar, status: &mut S, parent: &str) -> Self { + fn tracking_from(__sv_bar: v1::Bar, status: &mut __SvStatus, parent: &str) -> Self { use ::stackable_versioned::TrackingInto as _; match __sv_bar { v1::Bar::Baz => v2::Bar::Baz, @@ -390,11 +442,11 @@ where } } #[automatically_derived] -impl ::stackable_versioned::TrackingFrom for v1::Bar +impl<__SvStatus> ::stackable_versioned::TrackingFrom for v1::Bar where - S: ::stackable_versioned::TrackingStatus + ::core::default::Default, + __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { - fn tracking_from(__sv_bar: v2::Bar, status: &mut S, parent: &str) -> Self { + fn tracking_from(__sv_bar: v2::Bar, status: &mut __SvStatus, parent: &str) -> Self { use ::stackable_versioned::TrackingInto as _; match __sv_bar { v2::Bar::Baz => v1::Bar::Baz, diff --git a/crates/stackable-versioned-macros/tests/trybuild.rs b/crates/stackable-versioned-macros/tests/trybuild.rs index ce9bbd298..2c998f310 100644 --- a/crates/stackable-versioned-macros/tests/trybuild.rs +++ b/crates/stackable-versioned-macros/tests/trybuild.rs @@ -22,6 +22,7 @@ mod inputs { // mod conversion_hints; // mod conversion_tracking_hints; // mod conversion_tracking_enum; + // mod conversion_tracking_generics; // mod conversion_tracking; // mod crate_overrides; // mod crate_overrides_only_kube; diff --git a/crates/stackable-versioned/CHANGELOG.md b/crates/stackable-versioned/CHANGELOG.md index 15ca793f8..f8dff1247 100644 --- a/crates/stackable-versioned/CHANGELOG.md +++ b/crates/stackable-versioned/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- Re-add support for generics in tracking impls ([#1304]). + +[#1304]: https://github.com/stackabletech/operator-rs/pull/1304 + ## [0.13.0] - 2026-10-14 ### Changed