diff --git a/Cargo.lock b/Cargo.lock index a1f305135..e8adf1755 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3210,6 +3210,11 @@ dependencies = [ name = "stackable-versioned" version = "0.7.1" dependencies = [ + "k8s-version", + "schemars", + "serde", + "serde_json", + "serde_yaml", "stackable-versioned-macros", ] diff --git a/crates/k8s-version/src/api_version/serde.rs b/crates/k8s-version/src/api_version/serde.rs index f9754c842..2ef14822b 100644 --- a/crates/k8s-version/src/api_version/serde.rs +++ b/crates/k8s-version/src/api_version/serde.rs @@ -11,7 +11,7 @@ impl<'de> Deserialize<'de> for ApiVersion { { struct ApiVersionVisitor; - impl<'de> Visitor<'de> for ApiVersionVisitor { + impl Visitor<'_> for ApiVersionVisitor { type Value = ApiVersion; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { diff --git a/crates/k8s-version/src/level/serde.rs b/crates/k8s-version/src/level/serde.rs index 59bda5702..4d1fd8075 100644 --- a/crates/k8s-version/src/level/serde.rs +++ b/crates/k8s-version/src/level/serde.rs @@ -11,7 +11,7 @@ impl<'de> Deserialize<'de> for Level { { struct LevelVisitor; - impl<'de> Visitor<'de> for LevelVisitor { + impl Visitor<'_> for LevelVisitor { type Value = Level; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { diff --git a/crates/k8s-version/src/version/serde.rs b/crates/k8s-version/src/version/serde.rs index 88c7d98c3..c0f3b3c6c 100644 --- a/crates/k8s-version/src/version/serde.rs +++ b/crates/k8s-version/src/version/serde.rs @@ -11,7 +11,7 @@ impl<'de> Deserialize<'de> for Version { { struct VersionVisitor; - impl<'de> Visitor<'de> for VersionVisitor { + impl Visitor<'_> for VersionVisitor { type Value = Version; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { diff --git a/crates/stackable-versioned-macros/Cargo.toml b/crates/stackable-versioned-macros/Cargo.toml index 123679118..4b4996642 100644 --- a/crates/stackable-versioned-macros/Cargo.toml +++ b/crates/stackable-versioned-macros/Cargo.toml @@ -43,8 +43,9 @@ quote.workspace = true [dev-dependencies] # Only needed for doc tests / examples stackable-versioned = { path = "../stackable-versioned", features = ["k8s"] } -k8s-openapi.workspace = true +k8s-openapi.workspace = true +kube.workspace = true insta.workspace = true prettyplease.workspace = true regex.workspace = true diff --git a/crates/stackable-versioned-macros/fixtures/inputs/default/convert_with.rs b/crates/stackable-versioned-macros/fixtures/inputs/default/convert_with.rs index 8055f961a..e5e494656 100644 --- a/crates/stackable-versioned-macros/fixtures/inputs/default/convert_with.rs +++ b/crates/stackable-versioned-macros/fixtures/inputs/default/convert_with.rs @@ -5,8 +5,8 @@ struct Foo { // This tests two additional things: // - that both unquoted and quoted usage works // - that the renamed name does get picked up correctly by the conversion function - changed(since = "v1", from_type = "u16", from_name = "bar", convert_with = u16_to_u32), - changed(since = "v2", from_type = "u32", convert_with = "u32_to_u64") + changed(since = "v1", from_type = "u16", from_name = "bar", upgrade_with = u16_to_u32), + changed(since = "v2", from_type = "u32", upgrade_with = "u32_to_u64") )] baz: u64, } diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@basic_struct.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@basic_struct.rs.snap index 93dea7303..77df445d0 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@basic_struct.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@basic_struct.rs.snap @@ -24,6 +24,16 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + Self { + jjj: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] pub(crate) mod v1beta1 { use super::*; pub struct Foo { @@ -43,6 +53,15 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] pub(crate) mod v1 { use super::*; pub struct Foo { @@ -64,6 +83,17 @@ impl ::std::convert::From for v2::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v1::Foo { + fn from(__sv_foo: v2::Foo) -> Self { + Self { + foo: __sv_foo.foo.into(), + bar: __sv_foo.deprecated_bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] pub(crate) mod v2 { use super::*; pub struct Foo { @@ -86,6 +116,17 @@ impl ::std::convert::From for v3::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v2::Foo { + fn from(__sv_foo: v3::Foo) -> Self { + Self { + foo: __sv_foo.foo.into(), + deprecated_bar: __sv_foo.deprecated_bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] pub(crate) mod v3 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@convert_with.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@convert_with.rs.snap index ae5a50e8e..175ca2bae 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@convert_with.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@convert_with.rs.snap @@ -19,6 +19,12 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { bar: __sv_foo.baz.into() } + } +} +#[automatically_derived] mod v1 { use super::*; pub struct Foo { @@ -34,6 +40,12 @@ impl ::std::convert::From for v2::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1::Foo { + fn from(__sv_foo: v2::Foo) -> Self { + Self { baz: __sv_foo.baz.into() } + } +} +#[automatically_derived] mod v2 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_enum.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_enum.rs.snap index df0e35530..f65c81d4b 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_enum.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_enum.rs.snap @@ -21,6 +21,15 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + match __sv_foo { + v1beta1::Foo::Bar => v1alpha1::Foo::Bar, + v1beta1::Foo::Baz => v1alpha1::Foo::Baz, + } + } +} +#[automatically_derived] mod v1beta1 { use super::*; pub enum Foo { @@ -39,6 +48,15 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + match __sv_foo { + v1::Foo::DeprecatedBar => v1beta1::Foo::Bar, + v1::Foo::Baz => v1beta1::Foo::Baz, + } + } +} +#[automatically_derived] mod v1 { use super::*; pub enum Foo { @@ -58,6 +76,15 @@ impl ::std::convert::From for v2::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1::Foo { + fn from(__sv_foo: v2::Foo) -> Self { + match __sv_foo { + v2::Foo::DeprecatedBar => v1::Foo::DeprecatedBar, + v2::Foo::Baz => v1::Foo::Baz, + } + } +} +#[automatically_derived] mod v2 { use super::*; pub enum Foo { @@ -77,6 +104,15 @@ impl ::std::convert::From for v3::Foo { } } #[automatically_derived] +impl ::std::convert::From for v2::Foo { + fn from(__sv_foo: v3::Foo) -> Self { + match __sv_foo { + v3::Foo::DeprecatedBar => v2::Foo::DeprecatedBar, + v3::Foo::Baz => v2::Foo::Baz, + } + } +} +#[automatically_derived] mod v3 { use super::*; pub enum Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_struct.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_struct.rs.snap index c23badb37..0a49feef6 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_struct.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_struct.rs.snap @@ -21,6 +21,15 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] mod v1beta1 { use super::*; pub struct Foo { @@ -39,6 +48,16 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v1beta1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.deprecated_bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] mod v1 { use super::*; pub struct Foo { @@ -58,6 +77,16 @@ impl ::std::convert::From for v2::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v1::Foo { + fn from(__sv_foo: v2::Foo) -> Self { + Self { + deprecated_bar: __sv_foo.deprecated_bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] mod v2 { use super::*; pub struct Foo { @@ -77,6 +106,16 @@ impl ::std::convert::From for v3::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v2::Foo { + fn from(__sv_foo: v3::Foo) -> Self { + Self { + deprecated_bar: __sv_foo.deprecated_bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] mod v3 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@enum_data_simple.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@enum_data_simple.rs.snap index d945ef961..d1dc42f2e 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@enum_data_simple.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@enum_data_simple.rs.snap @@ -21,6 +21,15 @@ impl ::std::convert::From for v1alpha2::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1alpha2::Foo) -> Self { + match __sv_foo { + v1alpha2::Foo::Foo => v1alpha1::Foo::Foo, + v1alpha2::Foo::Bar(__sv_0, __sv_1) => v1alpha1::Foo::Bar(__sv_0, __sv_1), + } + } +} +#[automatically_derived] mod v1alpha2 { use super::*; pub enum Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_defaults.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_defaults.rs.snap index aa4910bda..2bba1d1c8 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_defaults.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_defaults.rs.snap @@ -27,6 +27,18 @@ where } } #[automatically_derived] +impl ::std::convert::From> for v1alpha1::Foo +where + T: Default, +{ + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] pub mod v1 { use super::*; pub struct Foo diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_enum.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_enum.rs.snap index 8adccda04..e344ae245 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_enum.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_enum.rs.snap @@ -27,6 +27,18 @@ where } } #[automatically_derived] +impl ::std::convert::From> for v1alpha1::Foo +where + T: Default, +{ + fn from(__sv_foo: v1::Foo) -> Self { + match __sv_foo { + v1::Foo::Bar(__sv_0) => v1alpha1::Foo::Bar(__sv_0), + v1::Foo::Baz => v1alpha1::Foo::Baz, + } + } +} +#[automatically_derived] pub mod v1 { use super::*; pub enum Foo diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_module.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_module.rs.snap index 72c56daa2..2f53ac95e 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_module.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_module.rs.snap @@ -33,6 +33,17 @@ pub mod versioned { } } } + impl ::std::convert::From> for v1alpha1::Foo + where + T: Default, + { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } + } impl ::std::convert::From> for v1::Boom where T: Default, @@ -44,6 +55,17 @@ pub mod versioned { } } } + impl ::std::convert::From> for v1alpha1::Boom + where + T: Default, + { + fn from(__sv_boom: v1::Boom) -> Self { + match __sv_boom { + v1::Boom::Big(__sv_0) => v1alpha1::Boom::Big(__sv_0), + v1::Boom::Shaq => v1alpha1::Boom::Shaq, + } + } + } pub mod v1 { use super::*; pub struct Foo diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_struct.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_struct.rs.snap index 76781812f..ae4d5417a 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_struct.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_struct.rs.snap @@ -27,6 +27,18 @@ where } } #[automatically_derived] +impl ::std::convert::From> for v1alpha1::Foo +where + T: Default, +{ + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] pub mod v1 { use super::*; pub struct Foo diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module.rs.snap index 5b91b66f9..aa1e55701 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module.rs.snap @@ -25,12 +25,27 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + foo: __sv_foo.foo.into(), + } + } +} +#[automatically_derived] impl ::std::convert::From for v1::Bar { fn from(__sv_bar: v1alpha1::Bar) -> Self { Self { baz: __sv_bar.baz.into() } } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Bar { + fn from(__sv_bar: v1::Bar) -> Self { + Self { baz: __sv_bar.baz.into() } + } +} +#[automatically_derived] pub(crate) mod v1 { use super::*; pub struct Foo { @@ -54,12 +69,29 @@ impl ::std::convert::From for v2alpha1::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v1::Foo { + fn from(__sv_foo: v2alpha1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + foo: __sv_foo.deprecated_foo.into(), + } + } +} +#[automatically_derived] impl ::std::convert::From for v2alpha1::Bar { fn from(__sv_bar: v1::Bar) -> Self { Self { baz: __sv_bar.baz.into() } } } #[automatically_derived] +impl ::std::convert::From for v1::Bar { + fn from(__sv_bar: v2alpha1::Bar) -> Self { + Self { baz: __sv_bar.baz.into() } + } +} +#[automatically_derived] pub(crate) mod v2alpha1 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module_preserve.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module_preserve.rs.snap index b74af9f05..00eef3c5c 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module_preserve.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module_preserve.rs.snap @@ -24,11 +24,24 @@ pub(crate) mod versioned { } } } + impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + foo: __sv_foo.foo.into(), + } + } + } impl ::std::convert::From for v1::Bar { fn from(__sv_bar: v1alpha1::Bar) -> Self { Self { baz: __sv_bar.baz.into() } } } + impl ::std::convert::From for v1alpha1::Bar { + fn from(__sv_bar: v1::Bar) -> Self { + Self { baz: __sv_bar.baz.into() } + } + } pub mod v1 { use super::*; pub struct Foo { @@ -50,11 +63,26 @@ pub(crate) mod versioned { } } } + #[allow(deprecated)] + impl ::std::convert::From for v1::Foo { + fn from(__sv_foo: v2alpha1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + foo: __sv_foo.deprecated_foo.into(), + } + } + } impl ::std::convert::From for v2alpha1::Bar { fn from(__sv_bar: v1::Bar) -> Self { Self { baz: __sv_bar.baz.into() } } } + impl ::std::convert::From for v1::Bar { + fn from(__sv_bar: v2alpha1::Bar) -> Self { + Self { baz: __sv_bar.baz.into() } + } + } pub mod v2alpha1 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@rename.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@rename.rs.snap index bf200c9f5..13e8a9d93 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@rename.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@rename.rs.snap @@ -21,6 +21,15 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + Self { + bat: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] mod v1beta1 { use super::*; pub struct Foo { @@ -38,6 +47,15 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] mod v1 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_for_version.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_for_version.rs.snap index 1dd6c5a6a..254d0466b 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_for_version.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_for_version.rs.snap @@ -20,6 +20,12 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + Self { baz: __sv_foo.baz.into() } + } +} +#[automatically_derived] pub mod v1beta1 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_module_for_version.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_module_for_version.rs.snap index 8db99d15c..a04ee29a0 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_module_for_version.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_module_for_version.rs.snap @@ -23,6 +23,12 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + Self { bar: __sv_foo.bar.into() } + } +} +#[automatically_derived] impl ::std::convert::From for v1beta1::Bar { fn from(__sv_bar: v1alpha1::Bar) -> Self { Self { @@ -32,6 +38,12 @@ impl ::std::convert::From for v1beta1::Bar { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Bar { + fn from(__sv_bar: v1beta1::Bar) -> Self { + Self { foo: __sv_bar.foo.into() } + } +} +#[automatically_derived] pub(crate) mod v1beta1 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@submodule.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@submodule.rs.snap index 7b6b9bc3d..f69c2970b 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@submodule.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@submodule.rs.snap @@ -18,6 +18,12 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { bar: __sv_foo.bar.into() } + } +} +#[automatically_derived] mod v1 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@basic.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@basic.rs.snap index 83bab4878..a9b098bb5 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@basic.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@basic.rs.snap @@ -20,7 +20,8 @@ pub(crate) mod v1alpha1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub baz: bool, @@ -36,6 +37,14 @@ impl ::std::convert::From for v1beta1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1beta1::FooSpec) -> Self { + Self { + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub(crate) mod v1beta1 { use super::*; #[derive( @@ -52,7 +61,8 @@ pub(crate) mod v1beta1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bah: u16, @@ -69,6 +79,15 @@ impl ::std::convert::From for v1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bah: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub(crate) mod v1 { use super::*; #[derive( @@ -85,7 +104,8 @@ pub(crate) mod v1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -130,3 +150,14 @@ impl Foo { ) } } +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct FooStatus { + pub crd_values: ::stackable_versioned::CrdValues, +} diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@crate_overrides.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@crate_overrides.rs.snap index 2999586ad..c30e7caa9 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@crate_overrides.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@crate_overrides.rs.snap @@ -21,7 +21,8 @@ pub mod v1alpha1 { singular = "foo", plural = "foos", namespaced, - crates(kube_core = ::kube::core) + crates(kube_core = ::kube::core), + status = FooStatus )] pub struct FooSpec { pub baz: bool, @@ -37,6 +38,14 @@ impl ::std::convert::From for v1beta1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1beta1::FooSpec) -> Self { + Self { + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub mod v1beta1 { use super::*; #[derive( @@ -54,7 +63,8 @@ pub mod v1beta1 { singular = "foo", plural = "foos", namespaced, - crates(kube_core = ::kube::core) + crates(kube_core = ::kube::core), + status = FooStatus )] pub struct FooSpec { pub bah: u16, @@ -71,6 +81,15 @@ impl ::std::convert::From for v1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bah: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub mod v1 { use super::*; #[derive( @@ -88,7 +107,8 @@ pub mod v1 { singular = "foo", plural = "foos", namespaced, - crates(kube_core = ::kube::core) + crates(kube_core = ::kube::core), + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -133,3 +153,14 @@ impl Foo { ) } } +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct FooStatus { + pub crd_values: ::stackable_versioned::CrdValues, +} diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module.rs.snap index d01dbc544..be85bce45 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module.rs.snap @@ -22,7 +22,8 @@ pub(crate) mod v1alpha1 { version = "v1alpha1", kind = "Foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -40,7 +41,8 @@ pub(crate) mod v1alpha1 { group = "bar.example.org", version = "v1alpha1", kind = "Bar", - plural = "bars" + plural = "bars", + status = BarStatus )] pub struct BarSpec { pub baz: String, @@ -57,6 +59,12 @@ impl ::std::convert::From for v1::Baz { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Baz { + fn from(__sv_baz: v1::Baz) -> Self { + Self { boom: __sv_baz.boom.into() } + } +} +#[automatically_derived] impl ::std::convert::From for v1::FooSpec { fn from(__sv_foospec: v1alpha1::FooSpec) -> Self { Self { @@ -67,6 +75,15 @@ impl ::std::convert::From for v1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bar: __sv_foospec.bar.into(), + foo: __sv_foospec.foo.into(), + } + } +} +#[automatically_derived] impl ::std::convert::From for v1::BarSpec { fn from(__sv_barspec: v1alpha1::BarSpec) -> Self { Self { @@ -75,6 +92,14 @@ impl ::std::convert::From for v1::BarSpec { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::BarSpec { + fn from(__sv_barspec: v1::BarSpec) -> Self { + Self { + baz: __sv_barspec.baz.into(), + } + } +} +#[automatically_derived] impl ::std::convert::From for v1::Boom { fn from(__sv_boom: v1alpha1::Boom) -> Self { match __sv_boom { @@ -84,6 +109,15 @@ impl ::std::convert::From for v1::Boom { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Boom { + fn from(__sv_boom: v1::Boom) -> Self { + match __sv_boom { + v1::Boom::Big => v1alpha1::Boom::Big, + v1::Boom::Shaq => v1alpha1::Boom::Shaq, + } + } +} +#[automatically_derived] pub(crate) mod v1 { use super::*; pub struct Baz { @@ -102,7 +136,8 @@ pub(crate) mod v1 { version = "v1", kind = "Foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -117,7 +152,13 @@ pub(crate) mod v1 { schemars::JsonSchema, kube::CustomResource, )] - #[kube(group = "bar.example.org", version = "v1", kind = "Bar", plural = "bars")] + #[kube( + group = "bar.example.org", + version = "v1", + kind = "Bar", + plural = "bars", + status = BarStatus + )] pub struct BarSpec { pub baz: String, } @@ -133,6 +174,12 @@ impl ::std::convert::From for v2alpha1::Baz { } } #[automatically_derived] +impl ::std::convert::From for v1::Baz { + fn from(__sv_baz: v2alpha1::Baz) -> Self { + Self { boom: __sv_baz.boom.into() } + } +} +#[automatically_derived] #[allow(deprecated)] impl ::std::convert::From for v2alpha1::FooSpec { fn from(__sv_foospec: v1::FooSpec) -> Self { @@ -144,6 +191,17 @@ impl ::std::convert::From for v2alpha1::FooSpec { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v1::FooSpec { + fn from(__sv_foospec: v2alpha1::FooSpec) -> Self { + Self { + bar: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + foo: __sv_foospec.deprecated_foo.into(), + } + } +} +#[automatically_derived] impl ::std::convert::From for v2alpha1::BarSpec { fn from(__sv_barspec: v1::BarSpec) -> Self { Self { @@ -152,6 +210,14 @@ impl ::std::convert::From for v2alpha1::BarSpec { } } #[automatically_derived] +impl ::std::convert::From for v1::BarSpec { + fn from(__sv_barspec: v2alpha1::BarSpec) -> Self { + Self { + baz: __sv_barspec.baz.into(), + } + } +} +#[automatically_derived] impl ::std::convert::From for v2alpha1::Boom { fn from(__sv_boom: v1::Boom) -> Self { match __sv_boom { @@ -161,6 +227,15 @@ impl ::std::convert::From for v2alpha1::Boom { } } #[automatically_derived] +impl ::std::convert::From for v1::Boom { + fn from(__sv_boom: v2alpha1::Boom) -> Self { + match __sv_boom { + v2alpha1::Boom::Big => v1::Boom::Big, + v2alpha1::Boom::Shaq => v1::Boom::Shaq, + } + } +} +#[automatically_derived] pub(crate) mod v2alpha1 { use super::*; pub struct Baz { @@ -179,7 +254,8 @@ pub(crate) mod v2alpha1 { version = "v2alpha1", kind = "Foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -199,7 +275,8 @@ pub(crate) mod v2alpha1 { group = "bar.example.org", version = "v2alpha1", kind = "Bar", - plural = "bars" + plural = "bars", + status = BarStatus )] pub struct BarSpec { pub baz: String, @@ -247,6 +324,17 @@ impl Foo { ) } } +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct FooStatus { + pub crd_values: ::stackable_versioned::CrdValues, +} #[automatically_derived] pub(crate) enum Bar { V1Alpha1, @@ -285,3 +373,14 @@ impl Bar { ) } } +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct BarStatus { + pub crd_values: ::stackable_versioned::CrdValues, +} diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module_preserve.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module_preserve.rs.snap index 601a8a0a9..6df68fd8c 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module_preserve.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module_preserve.rs.snap @@ -23,7 +23,8 @@ pub(crate) mod versioned { version = "v1alpha1", kind = "Foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -41,7 +42,8 @@ pub(crate) mod versioned { group = "bar.example.org", version = "v1alpha1", kind = "Bar", - plural = "bars" + plural = "bars", + status = BarStatus )] pub struct BarSpec { pub baz: String, @@ -56,6 +58,11 @@ pub(crate) mod versioned { Self { boom: __sv_baz.boom.into() } } } + impl ::std::convert::From for v1alpha1::Baz { + fn from(__sv_baz: v1::Baz) -> Self { + Self { boom: __sv_baz.boom.into() } + } + } impl ::std::convert::From for v1::FooSpec { fn from(__sv_foospec: v1alpha1::FooSpec) -> Self { Self { @@ -65,6 +72,14 @@ pub(crate) mod versioned { } } } + impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bar: __sv_foospec.bar.into(), + foo: __sv_foospec.foo.into(), + } + } + } impl ::std::convert::From for v1::BarSpec { fn from(__sv_barspec: v1alpha1::BarSpec) -> Self { Self { @@ -72,6 +87,13 @@ pub(crate) mod versioned { } } } + impl ::std::convert::From for v1alpha1::BarSpec { + fn from(__sv_barspec: v1::BarSpec) -> Self { + Self { + baz: __sv_barspec.baz.into(), + } + } + } impl ::std::convert::From for v1::Boom { fn from(__sv_boom: v1alpha1::Boom) -> Self { match __sv_boom { @@ -80,6 +102,14 @@ pub(crate) mod versioned { } } } + impl ::std::convert::From for v1alpha1::Boom { + fn from(__sv_boom: v1::Boom) -> Self { + match __sv_boom { + v1::Boom::Big => v1alpha1::Boom::Big, + v1::Boom::Shaq => v1alpha1::Boom::Shaq, + } + } + } pub mod v1 { use super::*; pub struct Baz { @@ -98,7 +128,8 @@ pub(crate) mod versioned { version = "v1", kind = "Foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -113,7 +144,13 @@ pub(crate) mod versioned { schemars::JsonSchema, kube::CustomResource, )] - #[kube(group = "bar.example.org", version = "v1", kind = "Bar", plural = "bars")] + #[kube( + group = "bar.example.org", + version = "v1", + kind = "Bar", + plural = "bars", + status = BarStatus + )] pub struct BarSpec { pub baz: String, } @@ -127,6 +164,11 @@ pub(crate) mod versioned { Self { boom: __sv_baz.boom.into() } } } + impl ::std::convert::From for v1::Baz { + fn from(__sv_baz: v2alpha1::Baz) -> Self { + Self { boom: __sv_baz.boom.into() } + } + } #[allow(deprecated)] impl ::std::convert::From for v2alpha1::FooSpec { fn from(__sv_foospec: v1::FooSpec) -> Self { @@ -137,6 +179,16 @@ pub(crate) mod versioned { } } } + #[allow(deprecated)] + impl ::std::convert::From for v1::FooSpec { + fn from(__sv_foospec: v2alpha1::FooSpec) -> Self { + Self { + bar: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + foo: __sv_foospec.deprecated_foo.into(), + } + } + } impl ::std::convert::From for v2alpha1::BarSpec { fn from(__sv_barspec: v1::BarSpec) -> Self { Self { @@ -144,6 +196,13 @@ pub(crate) mod versioned { } } } + impl ::std::convert::From for v1::BarSpec { + fn from(__sv_barspec: v2alpha1::BarSpec) -> Self { + Self { + baz: __sv_barspec.baz.into(), + } + } + } impl ::std::convert::From for v2alpha1::Boom { fn from(__sv_boom: v1::Boom) -> Self { match __sv_boom { @@ -152,6 +211,14 @@ pub(crate) mod versioned { } } } + impl ::std::convert::From for v1::Boom { + fn from(__sv_boom: v2alpha1::Boom) -> Self { + match __sv_boom { + v2alpha1::Boom::Big => v1::Boom::Big, + v2alpha1::Boom::Shaq => v1::Boom::Shaq, + } + } + } pub mod v2alpha1 { use super::*; pub struct Baz { @@ -170,7 +237,8 @@ pub(crate) mod versioned { version = "v2alpha1", kind = "Foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -190,7 +258,8 @@ pub(crate) mod versioned { group = "bar.example.org", version = "v2alpha1", kind = "Bar", - plural = "bars" + plural = "bars", + status = BarStatus )] pub struct BarSpec { pub baz: String, @@ -235,6 +304,17 @@ pub(crate) mod versioned { ) } } + #[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema + )] + #[serde(rename_all = "camelCase")] + pub struct FooStatus { + pub crd_values: ::stackable_versioned::CrdValues, + } pub enum Bar { V1Alpha1, V1, @@ -270,4 +350,15 @@ pub(crate) mod versioned { ) } } + #[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema + )] + #[serde(rename_all = "camelCase")] + pub struct BarStatus { + pub crd_values: ::stackable_versioned::CrdValues, + } } diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@renamed_kind.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@renamed_kind.rs.snap index fbda4713a..e96a7decf 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@renamed_kind.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@renamed_kind.rs.snap @@ -20,7 +20,8 @@ pub mod v1alpha1 { kind = "FooBar", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooBarStatus )] pub struct FooSpec { pub baz: bool, @@ -36,6 +37,14 @@ impl ::std::convert::From for v1beta1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1beta1::FooSpec) -> Self { + Self { + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub mod v1beta1 { use super::*; #[derive( @@ -52,7 +61,8 @@ pub mod v1beta1 { kind = "FooBar", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooBarStatus )] pub struct FooSpec { pub bah: u16, @@ -69,6 +79,15 @@ impl ::std::convert::From for v1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bah: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub mod v1 { use super::*; #[derive( @@ -85,7 +104,8 @@ pub mod v1 { kind = "FooBar", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooBarStatus )] pub struct FooSpec { pub bar: usize, @@ -130,3 +150,14 @@ impl FooBar { ) } } +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct FooBarStatus { + pub crd_values: ::stackable_versioned::CrdValues, +} diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@skip.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@skip.rs.snap index 39f0b2263..118f1ae44 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@skip.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@skip.rs.snap @@ -20,7 +20,8 @@ pub mod v1alpha1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub baz: bool, @@ -36,6 +37,14 @@ impl ::std::convert::From for v1beta1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1beta1::FooSpec) -> Self { + Self { + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub mod v1beta1 { use super::*; #[derive( @@ -52,7 +61,8 @@ pub mod v1beta1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bah: u16, @@ -69,6 +79,15 @@ impl ::std::convert::From for v1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bah: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub mod v1 { use super::*; #[derive( @@ -85,10 +104,22 @@ pub mod v1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, pub baz: bool, } } +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct FooStatus { + pub crd_values: ::stackable_versioned::CrdValues, +} diff --git a/crates/stackable-versioned-macros/src/attrs/item/mod.rs b/crates/stackable-versioned-macros/src/attrs/item/mod.rs index 34dbb3986..e82b2d3fe 100644 --- a/crates/stackable-versioned-macros/src/attrs/item/mod.rs +++ b/crates/stackable-versioned-macros/src/attrs/item/mod.rs @@ -240,13 +240,13 @@ impl CommonItemAttributes { // The convert_with argument only makes sense to use when the // type changed - if let Some(convert_func) = change.convert_with.as_ref() { + if let Some(upgrade_func) = change.upgrade_with.as_ref() { if change.from_type.is_none() { errors.push( Error::custom( "the `convert_with` argument must be used in combination with `from_type`", ) - .with_span(&convert_func.span()), + .with_span(&upgrade_func.span()), ); } } @@ -315,7 +315,8 @@ impl CommonItemAttributes { .unwrap_or(ty.clone()); actions.insert(*change.since, ItemStatus::Change { - convert_with: change.convert_with.as_deref().cloned(), + downgrade_with: change.downgrade_with.as_deref().cloned(), + upgrade_with: change.upgrade_with.as_deref().cloned(), from_ident: from_ident.clone(), from_type: from_ty.clone(), to_ident: ident, @@ -359,7 +360,8 @@ impl CommonItemAttributes { .unwrap_or(ty.clone()); actions.insert(*change.since, ItemStatus::Change { - convert_with: change.convert_with.as_deref().cloned(), + downgrade_with: change.downgrade_with.as_deref().cloned(), + upgrade_with: change.upgrade_with.as_deref().cloned(), from_ident: from_ident.clone(), from_type: from_ty.clone(), to_ident: ident, @@ -435,7 +437,8 @@ pub struct ChangedAttributes { pub since: SpannedValue, pub from_name: Option>, pub from_type: Option>, - pub convert_with: Option>, + pub upgrade_with: Option>, + pub downgrade_with: Option>, } /// For the deprecated() action diff --git a/crates/stackable-versioned-macros/src/attrs/k8s.rs b/crates/stackable-versioned-macros/src/attrs/k8s.rs index c241b17f7..b48def5ef 100644 --- a/crates/stackable-versioned-macros/src/attrs/k8s.rs +++ b/crates/stackable-versioned-macros/src/attrs/k8s.rs @@ -61,9 +61,10 @@ pub(crate) struct KubernetesSkipArguments { /// This struct contains crate overrides to be passed to `#[kube]`. #[derive(Clone, Debug, FromMeta)] pub(crate) struct KubernetesCrateArguments { - pub(crate) kube_core: Option, - pub(crate) k8s_openapi: Option, - pub(crate) schemars: Option, - pub(crate) serde: Option, - pub(crate) serde_json: Option, + pub kube_core: Option, + pub k8s_openapi: Option, + pub schemars: Option, + pub serde: Option, + pub serde_json: Option, + pub versioned: Option, } diff --git a/crates/stackable-versioned-macros/src/codegen/container/enum.rs b/crates/stackable-versioned-macros/src/codegen/container/enum.rs index da28cc785..8a7b8ce2b 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/enum.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/enum.rs @@ -1,6 +1,6 @@ use std::ops::Not; -use darling::{FromAttributes, Result, util::IdentString}; +use darling::{FromAttributes, Result}; use proc_macro2::TokenStream; use quote::quote; use syn::{Generics, ItemEnum}; @@ -126,11 +126,11 @@ impl Enum { } /// Generates code for the `From for NextVersion` implementation. - pub(crate) fn generate_from_impl( + pub fn generate_upgrade_from_impl( &self, version: &VersionDefinition, next_version: Option<&VersionDefinition>, - is_nested: bool, + add_attributes: bool, ) -> Option { if version.skip_from || self.common.options.skip_from { return None; @@ -145,12 +145,18 @@ impl Enum { // later versions. let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl(); let enum_ident = &self.common.idents.original; - let from_ident = &self.common.idents.from; + let from_enum_ident = &self.common.idents.from; - let next_version_ident = &next_version.ident; - let version_ident = &version.ident; + let for_module_ident = &next_version.ident; + let from_module_ident = &version.ident; - let variants = self.generate_from_variants(version, next_version, enum_ident); + let variants: TokenStream = self + .variants + .iter() + .filter_map(|v| { + v.generate_for_upgrade_from_impl(version, next_version, enum_ident) + }) + .collect(); // Include allow(deprecated) only when this or the next version is // deprecated. Also include it, when a variant in this or the next @@ -163,17 +169,18 @@ impl Enum { // Only add the #[automatically_derived] attribute only if this impl is used // outside of a module (in standalone mode). - let automatically_derived = - is_nested.not().then(|| quote! {#[automatically_derived]}); + let automatically_derived = add_attributes + .not() + .then(|| quote! {#[automatically_derived]}); Some(quote! { #automatically_derived #allow_attribute - impl #impl_generics ::std::convert::From<#version_ident::#enum_ident #type_generics> for #next_version_ident::#enum_ident #type_generics + impl #impl_generics ::std::convert::From<#from_module_ident::#enum_ident #type_generics> for #for_module_ident::#enum_ident #type_generics #where_clause { - fn from(#from_ident: #version_ident::#enum_ident #type_generics) -> Self { - match #from_ident { + fn from(#from_enum_ident: #from_module_ident::#enum_ident #type_generics) -> Self { + match #from_enum_ident { #variants } } @@ -184,20 +191,54 @@ impl Enum { } } - /// Generates code for enum variants used in `From` implementations. - fn generate_from_variants( + pub fn generate_downgrade_from_impl( &self, version: &VersionDefinition, - next_version: &VersionDefinition, - enum_ident: &IdentString, - ) -> TokenStream { - let mut tokens = TokenStream::new(); - - for variant in &self.variants { - tokens.extend(variant.generate_for_from_impl(version, next_version, enum_ident)); + next_version: Option<&VersionDefinition>, + add_attributes: bool, + ) -> Option { + if version.skip_from || self.common.options.skip_from { + return None; } - tokens + match next_version { + Some(next_version) => { + let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl(); + let enum_ident = &self.common.idents.original; + let from_enum_ident = &self.common.idents.from; + + let for_module_ident = &version.ident; + let from_module_ident = &next_version.ident; + + let variants: TokenStream = self + .variants + .iter() + .filter_map(|v| { + v.generate_for_downgrade_from_impl(version, next_version, enum_ident) + }) + .collect(); + + // Only add the #[automatically_derived] attribute only if this impl is used + // outside of a module (in standalone mode). + let automatically_derived = add_attributes + .not() + .then(|| quote! {#[automatically_derived]}); + + Some(quote! { + #automatically_derived + impl #impl_generics ::std::convert::From<#from_module_ident::#enum_ident #type_generics> for #for_module_ident::#enum_ident #type_generics + #where_clause + { + fn from(#from_enum_ident: #from_module_ident::#enum_ident #type_generics) -> Self { + match #from_enum_ident { + #variants + } + } + } + }) + } + None => None, + } } /// Returns whether any variant is deprecated in the provided `version`. diff --git a/crates/stackable-versioned-macros/src/codegen/container/mod.rs b/crates/stackable-versioned-macros/src/codegen/container/mod.rs index fbbff4006..903f5f2fc 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/mod.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/mod.rs @@ -21,7 +21,7 @@ mod r#enum; mod r#struct; /// Contains common container data shared between structs and enums. -pub(crate) struct CommonContainerData { +pub struct CommonContainerData { /// Original attributes placed on the container, like `#[derive()]` or `#[cfg()]`. pub(crate) original_attributes: Vec, @@ -37,30 +37,55 @@ pub(crate) struct CommonContainerData { /// Abstracting away with kind of container is generated makes it possible to create a list of /// containers when the macro is used on modules. This enum provides functions to generate code /// which then internally call the appropriate function based on the variant. -pub(crate) enum Container { +pub enum Container { Struct(Struct), Enum(Enum), } impl Container { /// Generates the container definition for the specified `version`. - pub(crate) fn generate_definition(&self, version: &VersionDefinition) -> TokenStream { + pub(crate) fn generate_definition( + &self, + version: &VersionDefinition, + multiple_versions: bool, + ) -> TokenStream { match self { - Container::Struct(s) => s.generate_definition(version), + Container::Struct(s) => s.generate_definition(version, multiple_versions), Container::Enum(e) => e.generate_definition(version), } } - /// Generates the container `From for NextVersion` implementation. - pub(crate) fn generate_from_impl( + /// Generates the `From for NextVersion` implementation for the container. + pub fn generate_upgrade_from_impl( &self, version: &VersionDefinition, next_version: Option<&VersionDefinition>, add_attributes: bool, ) -> Option { match self { - Container::Struct(s) => s.generate_from_impl(version, next_version, add_attributes), - Container::Enum(e) => e.generate_from_impl(version, next_version, add_attributes), + Container::Struct(s) => { + s.generate_upgrade_from_impl(version, next_version, add_attributes) + } + Container::Enum(e) => { + e.generate_upgrade_from_impl(version, next_version, add_attributes) + } + } + } + + /// Generates the `From for Version` implementation for the container. + pub fn generate_downgrade_from_impl( + &self, + version: &VersionDefinition, + next_version: Option<&VersionDefinition>, + add_attributes: bool, + ) -> Option { + match self { + Container::Struct(s) => { + s.generate_downgrade_from_impl(version, next_version, add_attributes) + } + Container::Enum(e) => { + e.generate_downgrade_from_impl(version, next_version, add_attributes) + } } } @@ -74,7 +99,7 @@ impl Container { /// /// This function only returns `Some` if it is a struct. Enums cannot be used to define /// Kubernetes custom resources. - pub(crate) fn generate_kubernetes_item( + pub fn generate_kubernetes_item( &self, version: &VersionDefinition, ) -> Option<(IdentString, String, TokenStream)> { @@ -88,7 +113,7 @@ impl Container { /// /// This function only returns `Some` if it is a struct. Enums cannot be used to define /// Kubernetes custom resources. - pub(crate) fn generate_kubernetes_merge_crds( + pub fn generate_kubernetes_merge_crds( &self, enum_variant_idents: &[IdentString], enum_variant_strings: &[String], @@ -108,7 +133,14 @@ impl Container { } } - pub(crate) fn get_original_ident(&self) -> &Ident { + pub fn generate_kubernetes_status_struct(&self) -> Option { + match self { + Container::Struct(s) => s.generate_kubernetes_status_struct(), + Container::Enum(_) => None, + } + } + + pub fn get_original_ident(&self) -> &Ident { match &self { Container::Struct(s) => s.common.idents.original.as_ident(), Container::Enum(e) => e.common.idents.original.as_ident(), @@ -174,15 +206,26 @@ impl StandaloneContainer { let mut kubernetes_enum_variant_strings = Vec::new(); let mut versions = self.versions.iter().peekable(); + let multiple_versions = versions.len() > 1; while let Some(version) = versions.next() { - let container_definition = self.container.generate_definition(version); + let container_definition = self + .container + .generate_definition(version, multiple_versions); // NOTE (@Techassi): Using '.copied()' here does not copy or clone the data, but instead // removes one level of indirection of the double reference '&&'. - let from_impl = + let next_version = versions.peek().copied(); + + // Generate the From impl for upgrading the CRD. + let upgrade_from_impl = self.container - .generate_from_impl(version, versions.peek().copied(), false); + .generate_upgrade_from_impl(version, next_version, false); + + // Generate the From impl for downgrading the CRD. + let downgrade_from_impl = + self.container + .generate_downgrade_from_impl(version, next_version, false); // Add the #[deprecated] attribute when the version is marked as deprecated. let deprecated_attribute = version @@ -210,7 +253,8 @@ impl StandaloneContainer { #container_definition } - #from_impl + #upgrade_from_impl + #downgrade_from_impl }); } @@ -222,6 +266,10 @@ impl StandaloneContainer { false, )); + if multiple_versions { + tokens.extend(self.container.generate_kubernetes_status_struct()); + } + tokens } } @@ -231,13 +279,13 @@ impl StandaloneContainer { pub(crate) struct ContainerIdents { /// The ident used in the context of Kubernetes specific code. This ident /// removes the 'Spec' suffix present in the definition container. - pub(crate) kubernetes: IdentString, + pub kubernetes: IdentString, /// The original ident, or name, of the versioned container. - pub(crate) original: IdentString, + pub original: IdentString, /// The ident used in the [`From`] impl. - pub(crate) from: IdentString, + pub from: IdentString, } impl ContainerIdents { @@ -261,32 +309,32 @@ impl ContainerIdents { } #[derive(Debug)] -pub(crate) struct ContainerOptions { - pub(crate) kubernetes_options: Option, - pub(crate) skip_from: bool, +pub struct ContainerOptions { + pub kubernetes_options: Option, + pub skip_from: bool, } #[derive(Debug)] -pub(crate) struct KubernetesOptions { - pub(crate) group: String, - pub(crate) kind: Option, - pub(crate) singular: Option, - pub(crate) plural: Option, - pub(crate) namespaced: bool, +pub struct KubernetesOptions { + pub group: String, + pub kind: Option, + pub singular: Option, + pub plural: Option, + pub namespaced: bool, // root - pub(crate) crates: KubernetesCrateOptions, - pub(crate) status: Option, + pub crates: KubernetesCrateOptions, + pub status: Option, // derive // schema // scale // printcolumn - pub(crate) shortnames: Vec, + pub shortnames: Vec, // category // selectable // doc // annotation // label - pub(crate) skip_merged_crd: bool, + pub skip_merged_crd: bool, } impl From for KubernetesOptions { @@ -308,17 +356,19 @@ impl From for KubernetesOptions { } #[derive(Debug)] -pub(crate) struct KubernetesCrateOptions { - pub(crate) kube_core: Override, - pub(crate) k8s_openapi: Override, - pub(crate) schemars: Override, - pub(crate) serde: Override, - pub(crate) serde_json: Override, +pub struct KubernetesCrateOptions { + pub kube_core: Override, + pub k8s_openapi: Override, + pub schemars: Override, + pub serde: Override, + pub serde_json: Override, + pub versioned: Override, } impl Default for KubernetesCrateOptions { fn default() -> Self { Self { + versioned: Override::Default(parse_quote! { ::stackable_versioned }), k8s_openapi: Override::Default(parse_quote! { ::k8s_openapi }), serde_json: Override::Default(parse_quote! { ::serde_json }), kube_core: Override::Default(parse_quote! { ::kube::core }), @@ -352,6 +402,10 @@ impl From for KubernetesCrateOptions { crate_options.serde = Override::Overridden(serde); } + if let Some(versioned) = args.versioned { + crate_options.versioned = Override::Overridden(versioned); + } + crate_options } } @@ -366,6 +420,7 @@ impl ToTokens for KubernetesCrateOptions { kube_core, schemars, serde, + .. } = self; if let Override::Overridden(k8s_openapi) = k8s_openapi { @@ -396,7 +451,7 @@ impl ToTokens for KubernetesCrateOptions { /// Wraps a value to indicate whether it is original or has been overridden. #[derive(Debug)] -pub(crate) enum Override { +pub enum Override { Default(T), Overridden(T), } diff --git a/crates/stackable-versioned-macros/src/codegen/container/struct.rs b/crates/stackable-versioned-macros/src/codegen/container/struct.rs index 584a293b1..dfcb6f6bb 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/struct.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/struct.rs @@ -2,7 +2,7 @@ use std::ops::Not; use darling::{Error, FromAttributes, Result, util::IdentString}; use proc_macro2::TokenStream; -use quote::{ToTokens, quote}; +use quote::{ToTokens, format_ident, quote}; use syn::{Generics, ItemStruct, Path, Visibility, parse_quote}; use crate::{ @@ -119,7 +119,7 @@ impl Container { } /// A versioned struct. -pub(crate) struct Struct { +pub struct Struct { /// List of fields defined in the original struct. How, and if, an item /// should generate code, is decided by the currently generated version. pub fields: Vec, @@ -134,7 +134,11 @@ pub(crate) struct Struct { // Common token generation impl Struct { /// Generates code for the struct definition. - pub(crate) fn generate_definition(&self, version: &VersionDefinition) -> TokenStream { + pub fn generate_definition( + &self, + version: &VersionDefinition, + multiple_versions: bool, + ) -> TokenStream { let where_clause = self.generics.where_clause.as_ref(); let type_generics = &self.generics; @@ -148,7 +152,7 @@ impl Struct { } // This only returns Some, if K8s features are enabled - let kube_attribute = self.generate_kube_attribute(version); + let kube_attribute = self.generate_kube_attribute(version, multiple_versions); quote! { #(#[doc = #version_docs])* @@ -160,8 +164,13 @@ impl Struct { } } + // TODO (@Techassi): It looks like some of the stuff from the upgrade and downgrade functions + // can be combined into a single piece of code. Figure out a nice way to do that. /// Generates code for the `From for NextVersion` implementation. - pub(crate) fn generate_from_impl( + /// + /// The `add_attributes` parameter declares if attributes (macros) should be added to the + /// generated `From` impl block. + pub fn generate_upgrade_from_impl( &self, version: &VersionDefinition, next_version: Option<&VersionDefinition>, @@ -185,7 +194,13 @@ impl Struct { let for_module_ident = &next_version.ident; let from_module_ident = &version.ident; - let fields = self.generate_from_fields(version, next_version, from_struct_ident); + let fields: TokenStream = self + .fields + .iter() + .map(|f| { + f.generate_for_upgrade_from_impl(version, next_version, from_struct_ident) + }) + .collect(); // Include allow(deprecated) only when this or the next version is // deprecated. Also include it, when a field in this or the next @@ -220,20 +235,64 @@ impl Struct { } } - /// Generates code for struct fields used in `From` implementations. - fn generate_from_fields( + pub fn generate_downgrade_from_impl( &self, version: &VersionDefinition, - next_version: &VersionDefinition, - from_struct_ident: &IdentString, - ) -> TokenStream { - let mut tokens = TokenStream::new(); - - for field in &self.fields { - tokens.extend(field.generate_for_from_impl(version, next_version, from_struct_ident)); + next_version: Option<&VersionDefinition>, + add_attributes: bool, + ) -> Option { + if version.skip_from || self.common.options.skip_from { + return None; } - tokens + match next_version { + Some(next_version) => { + let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl(); + let struct_ident = &self.common.idents.original; + let from_struct_ident = &self.common.idents.from; + + let for_module_ident = &version.ident; + let from_module_ident = &next_version.ident; + + let fields: TokenStream = self + .fields + .iter() + .map(|f| { + f.generate_for_downgrade_from_impl(version, next_version, from_struct_ident) + }) + .collect(); + + // Include allow(deprecated) only when this or the next version is + // deprecated. Also include it, when a field in this or the next + // version is deprecated. + let allow_attribute = (version.deprecated.is_some() + || next_version.deprecated.is_some() + || self.is_any_field_deprecated(version) + || self.is_any_field_deprecated(next_version)) + .then(|| quote! { #[allow(deprecated)] }); + + // Only add the #[automatically_derived] attribute only if this impl is used + // outside of a module (in standalone mode). + let automatically_derived = add_attributes + .not() + .then(|| quote! {#[automatically_derived]}); + + Some(quote! { + #automatically_derived + #allow_attribute + impl #impl_generics ::std::convert::From<#from_module_ident::#struct_ident #type_generics> for #for_module_ident::#struct_ident #type_generics + #where_clause + { + fn from(#from_struct_ident: #from_module_ident::#struct_ident #type_generics) -> Self { + Self { + #fields + } + } + } + }) + } + None => None, + } } /// Returns whether any field is deprecated in the provided `version`. @@ -260,11 +319,14 @@ impl Struct { } } +// TODO (@Techassi): Somehow bundle this into one struct which can emit all K8s related code. This +// makes keeping track of interconnected parts easier. // Kubernetes-specific token generation impl Struct { - pub(crate) fn generate_kube_attribute( + pub fn generate_kube_attribute( &self, version: &VersionDefinition, + multiple_versions: bool, ) -> Option { match &self.common.options.kubernetes_options { Some(kubernetes_options) => { @@ -283,18 +345,30 @@ impl Struct { .singular .as_ref() .map(|s| quote! { , singular = #s }); + let plural = kubernetes_options .plural .as_ref() .map(|p| quote! { , plural = #p }); + let namespaced = kubernetes_options .namespaced .then_some(quote! { , namespaced }); let crates = kubernetes_options.crates.to_token_stream(); - let status = kubernetes_options - .status - .as_ref() - .map(|s| quote! { , status = #s }); + + let status = if multiple_versions { + let status_ident = format_ident!( + "{struct_ident}Status", + struct_ident = self.common.idents.kubernetes.as_ident() + ); + Some(quote! { , status = #status_ident }) + } else { + kubernetes_options + .status + .as_ref() + .map(|s| quote! { , status = #s }) + }; + let shortnames: TokenStream = kubernetes_options .shortnames .iter() @@ -316,13 +390,13 @@ impl Struct { } } - pub(crate) fn generate_kubernetes_item( + pub fn generate_kubernetes_item( &self, version: &VersionDefinition, ) -> Option<(IdentString, String, TokenStream)> { match &self.common.options.kubernetes_options { Some(options) if !options.skip_merged_crd => { - let kube_core_path = &*options.crates.kube_core; + let kube_core_crate = &*options.crates.kube_core; let enum_variant_ident = version.inner.as_variant_ident(); let enum_variant_string = version.inner.to_string(); @@ -332,7 +406,7 @@ impl Struct { let qualified_path: Path = parse_quote!(#module_ident::#struct_ident); let merge_crds_fn_call = quote! { - <#qualified_path as #kube_core_path::CustomResourceExt>::crd() + <#qualified_path as #kube_core_crate::CustomResourceExt>::crd() }; Some((enum_variant_ident, enum_variant_string, merge_crds_fn_call)) @@ -341,7 +415,7 @@ impl Struct { } } - pub(crate) fn generate_kubernetes_merge_crds( + pub fn generate_kubernetes_merge_crds( &self, enum_variant_idents: &[IdentString], enum_variant_strings: &[String], @@ -391,4 +465,52 @@ impl Struct { _ => None, } } + + pub fn generate_kubernetes_status_struct(&self) -> Option { + match &self.common.options.kubernetes_options { + Some(kubernetes_options) => { + let status_ident = format_ident!( + "{struct_ident}Status", + struct_ident = self.common.idents.kubernetes.as_ident() + ); + + let versioned_crate = &*kubernetes_options.crates.versioned; + let schemars_crate = &*kubernetes_options.crates.schemars; + let serde_crate = &*kubernetes_options.crates.serde; + + match &kubernetes_options.status { + Some(status) => Some(quote! { + #[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + #serde_crate::Deserialize, + #serde_crate::Serialize, + #schemars_crate::JsonSchema + )] + #[serde(rename_all = "camelCase")] + pub struct #status_ident { + pub crd_values: #versioned_crate::CrdValues, + + #[serde(flatten)] + pub status: #status, + } + }), + None => Some(quote! { + #[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + #serde_crate::Deserialize, + #serde_crate::Serialize, + #schemars_crate::JsonSchema + )] + #[serde(rename_all = "camelCase")] + pub struct #status_ident { + pub crd_values: #versioned_crate::CrdValues, + } + }), + } + } + None => None, + } + } } diff --git a/crates/stackable-versioned-macros/src/codegen/item/field.rs b/crates/stackable-versioned-macros/src/codegen/item/field.rs index f55f774fa..c039e9873 100644 --- a/crates/stackable-versioned-macros/src/codegen/item/field.rs +++ b/crates/stackable-versioned-macros/src/codegen/item/field.rs @@ -144,7 +144,7 @@ impl VersionedField { } // TODO (@Techassi): This should probably return an optional token stream. - pub(crate) fn generate_for_from_impl( + pub fn generate_for_upgrade_from_impl( &self, version: &VersionDefinition, next_version: &VersionDefinition, @@ -174,16 +174,16 @@ impl VersionedField { _, ItemStatus::Change { from_ident: old_field_ident, - convert_with, + upgrade_with, to_ident, .. }, - ) => match convert_with { + ) => match upgrade_with { // The user specified a custom conversion function which // will be used here instead of the default .into() call // which utilizes From impls. - Some(convert_fn) => quote! { - #to_ident: #convert_fn(#from_struct_ident.#old_field_ident), + Some(upgrade_fn) => quote! { + #to_ident: #upgrade_fn(#from_struct_ident.#old_field_ident), }, // Default .into() call using From impls. None => quote! { @@ -212,4 +212,62 @@ impl VersionedField { } } } + + pub fn generate_for_downgrade_from_impl( + &self, + version: &VersionDefinition, + next_version: &VersionDefinition, + from_struct_ident: &IdentString, + ) -> TokenStream { + match &self.changes { + Some(changes) => { + let next_change = changes.get_expect(&next_version.inner); + let change = changes.get_expect(&version.inner); + + match (change, next_change) { + // If both this status and the next one is NotPresent, which means + // a field was introduced after a bunch of versions, we don't + // need to generate any code for the From impl. + (ItemStatus::NotPresent, ItemStatus::NotPresent) => { + quote! {} + } + (_, ItemStatus::Addition { .. }) => quote! {}, + ( + _, + ItemStatus::Change { + downgrade_with, + from_ident: old_field_ident, + to_ident, + .. + }, + ) => match downgrade_with { + Some(downgrade_fn) => quote! { + #old_field_ident: #downgrade_fn(#from_struct_ident.#to_ident), + }, + None => quote! { + #old_field_ident: #from_struct_ident.#to_ident.into(), + }, + }, + (old, next) => { + let next_field_ident = next.get_ident(); + let old_field_ident = old.get_ident(); + + // NOTE (@Techassi): Do we really need .into() here. I'm + // currently not sure why it is there and if it is needed + // in some edge cases. + quote! { + #old_field_ident: #from_struct_ident.#next_field_ident.into(), + } + } + } + } + None => { + let field_ident = &*self.ident; + + quote! { + #field_ident: #from_struct_ident.#field_ident.into(), + } + } + } + } } diff --git a/crates/stackable-versioned-macros/src/codegen/item/variant.rs b/crates/stackable-versioned-macros/src/codegen/item/variant.rs index a65168c57..24c62f76b 100644 --- a/crates/stackable-versioned-macros/src/codegen/item/variant.rs +++ b/crates/stackable-versioned-macros/src/codegen/item/variant.rs @@ -133,40 +133,67 @@ impl VersionedVariant { } } - pub(crate) fn generate_for_from_impl( + pub fn generate_for_upgrade_from_impl( &self, version: &VersionDefinition, next_version: &VersionDefinition, enum_ident: &IdentString, ) -> Option { - let variant_fields = match &self.fields { - Fields::Named(fields_named) => { - let fields: Vec<_> = fields_named - .named - .iter() - .map(|field| { - field - .ident - .as_ref() - .expect("named fields always have an ident") - .clone() - }) - .collect(); + let variant_fields = self.fields_as_token_stream(); + + match &self.changes { + Some(changes) => { + let next_change = changes.get_expect(&next_version.inner); + let change = changes.get_expect(&version.inner); + + match (change, next_change) { + (_, ItemStatus::Addition { .. }) => None, + (old, next) => { + let next_version_ident = &next_version.ident; + let old_version_ident = &version.ident; - quote! { { #(#fields),* } } + let next_variant_ident = next.get_ident(); + let old_variant_ident = old.get_ident(); + + let old = quote! { + #old_version_ident::#enum_ident::#old_variant_ident #variant_fields + }; + let next = quote! { + #next_version_ident::#enum_ident::#next_variant_ident #variant_fields + }; + + Some(quote! { + #old => #next, + }) + } + } } - Fields::Unnamed(fields_unnamed) => { - let fields: Vec<_> = fields_unnamed - .unnamed - .iter() - .enumerate() - .map(|(index, _)| format_ident!("__sv_{index}")) - .collect(); + None => { + let next_version_ident = &next_version.ident; + let old_version_ident = &version.ident; + let variant_ident = &*self.ident; + + let old = quote! { + #old_version_ident::#enum_ident::#variant_ident #variant_fields + }; + let next = quote! { + #next_version_ident::#enum_ident::#variant_ident #variant_fields + }; - quote! { ( #(#fields),* ) } + Some(quote! { + #old => #next, + }) } - Fields::Unit => TokenStream::new(), - }; + } + } + + pub fn generate_for_downgrade_from_impl( + &self, + version: &VersionDefinition, + next_version: &VersionDefinition, + enum_ident: &IdentString, + ) -> Option { + let variant_fields = self.fields_as_token_stream(); match &self.changes { Some(changes) => { @@ -190,7 +217,7 @@ impl VersionedVariant { }; Some(quote! { - #old => #next, + #next => #old, }) } } @@ -208,9 +235,39 @@ impl VersionedVariant { }; Some(quote! { - #old => #next, + #next => #old, }) } } } + + fn fields_as_token_stream(&self) -> Option { + match &self.fields { + Fields::Named(fields_named) => { + let fields: Vec<_> = fields_named + .named + .iter() + .map(|field| { + field + .ident + .as_ref() + .expect("named fields always have an ident") + }) + .collect(); + + Some(quote! { { #(#fields),* } }) + } + Fields::Unnamed(fields_unnamed) => { + let fields: Vec<_> = fields_unnamed + .unnamed + .iter() + .enumerate() + .map(|(index, _)| format_ident!("__sv_{index}")) + .collect(); + + Some(quote! { ( #(#fields),* ) }) + } + Fields::Unit => None, + } + } } diff --git a/crates/stackable-versioned-macros/src/codegen/mod.rs b/crates/stackable-versioned-macros/src/codegen/mod.rs index 4f4b2ea3c..88bf340c6 100644 --- a/crates/stackable-versioned-macros/src/codegen/mod.rs +++ b/crates/stackable-versioned-macros/src/codegen/mod.rs @@ -81,7 +81,8 @@ pub enum ItemStatus { ty: Type, }, Change { - convert_with: Option, + downgrade_with: Option, + upgrade_with: Option, from_ident: IdentString, to_ident: IdentString, from_type: Type, @@ -107,7 +108,7 @@ impl ItemStatus { ItemStatus::Change { to_ident, .. } => to_ident, ItemStatus::Deprecation { ident, .. } => ident, ItemStatus::NoChange { ident, .. } => ident, - ItemStatus::NotPresent => unreachable!(), + ItemStatus::NotPresent => unreachable!("ItemStatus::NotPresent does not have an ident"), } } } diff --git a/crates/stackable-versioned-macros/src/codegen/module.rs b/crates/stackable-versioned-macros/src/codegen/module.rs index 217dacced..68b6eacb3 100644 --- a/crates/stackable-versioned-macros/src/codegen/module.rs +++ b/crates/stackable-versioned-macros/src/codegen/module.rs @@ -149,20 +149,29 @@ impl Module { let mut kubernetes_container_items: HashMap = HashMap::new(); let mut versions = self.versions.iter().peekable(); + let multiple_versions = self.versions.len() > 1; while let Some(version) = versions.next() { + let next_version = versions.peek().copied(); let mut container_definitions = TokenStream::new(); let mut from_impls = TokenStream::new(); let version_ident = &version.ident; for container in &self.containers { - container_definitions.extend(container.generate_definition(version)); + container_definitions + .extend(container.generate_definition(version, multiple_versions)); if !self.skip_from { - from_impls.extend(container.generate_from_impl( + from_impls.extend(container.generate_upgrade_from_impl( version, - versions.peek().copied(), + next_version, + self.preserve_module, + )); + + from_impls.extend(container.generate_downgrade_from_impl( + version, + next_version, self.preserve_module, )); } @@ -228,6 +237,8 @@ impl Module { version_module_vis, self.preserve_module, )); + + kubernetes_tokens.extend(container.generate_kubernetes_status_struct()); } } diff --git a/crates/stackable-versioned-macros/src/lib.rs b/crates/stackable-versioned-macros/src/lib.rs index 469ee61b0..a57292f8d 100644 --- a/crates/stackable-versioned-macros/src/lib.rs +++ b/crates/stackable-versioned-macros/src/lib.rs @@ -478,11 +478,16 @@ mod utils; /// #[versioned(changed( /// since = "v1beta1", /// from_name = "prev_bar", -/// from_type = "u16" +/// from_type = "u16", +/// downgrade_with = usize_to_u16 /// ))] /// bar: usize, /// baz: bool, /// } +/// +/// fn usize_to_u16(input: usize) -> u16 { +/// input.try_into().unwrap() +/// } /// ``` /// ///
@@ -591,8 +596,8 @@ mod utils; /// ### Custom conversion function at field level /// /// As stated in the [`changed()`](#changed-action) section, a custom conversion -/// function can be provided using the `convert_with` argument. A simple example -/// looks like this: +/// function can be provided using the `downgrade_with` and `upgrade_with` +/// argument. A simple example looks like this: /// /// ``` /// # use stackable_versioned_macros::versioned; @@ -604,13 +609,13 @@ mod utils; /// #[versioned(changed( /// since = "v1beta1", /// from_type = "u8", -/// convert_with = "u8_to_u16" +/// downgrade_with = "u16_to_u8" /// ))] /// bar: u16, /// } /// -/// fn u8_to_u16(old: u8) -> u16 { -/// old as u16 +/// fn u16_to_u8(input: u16) -> u8 { +/// input.try_into().unwrap() /// } /// ``` /// @@ -628,7 +633,15 @@ mod utils; /// impl ::std::convert::From for v1beta1::Foo { /// fn from(__sv_foo: v1alpha1::Foo) -> Self { /// Self { -/// bar: u8_to_u16(__sv_foo.bar), +/// bar: __sv_foo.bar.into(), +/// } +/// } +/// } +/// +/// impl ::std::convert::From for v1alpha1::Foo { +/// fn from(__sv_foo: v1beta1::Foo) -> Self { +/// Self { +/// bar: u16_to_u8(__sv_foo.bar), /// } /// } /// } @@ -718,12 +731,16 @@ use serde::{Deserialize, Serialize}; pub struct FooSpec { #[versioned( added(since = "v1beta1"), - changed(since = "v1", from_name = "prev_bar", from_type = "u16") + changed(since = "v1", from_name = "prev_bar", from_type = "u16", downgrade_with = usize_to_u16) )] bar: usize, baz: bool, } +fn usize_to_u16(input: usize) -> u16 { + input.try_into().unwrap() +} + # fn main() { let merged_crd = Foo::merged_crd(Foo::V1).unwrap(); println!("{}", serde_yaml::to_string(&merged_crd).unwrap()); diff --git a/crates/stackable-versioned/CHANGELOG.md b/crates/stackable-versioned/CHANGELOG.md index 9fee8c70c..0aa3abe84 100644 --- a/crates/stackable-versioned/CHANGELOG.md +++ b/crates/stackable-versioned/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- Implement basic ground work for downgrading custom resources ([#1033]). + - Emit `From` implementations to downgrade custom resource specs. + - Emit a status struct when multiple versions are defined to be able to track + values required during downgrades and upgrades of custom resources. + +### Changed + +- BREAKING: The `convert_with` parameter of the `changed()` action was renamed and split into two + parts to be able to control the conversion during upgrades and downgrades: `upgrade_with` and + `downgrade_with` ([#1033]). + +[#1033]: https://github.com/stackabletech/operator-rs/pull/1033 + ### Fixed - Correctly handle fields added in later versions ([#1031]). diff --git a/crates/stackable-versioned/Cargo.toml b/crates/stackable-versioned/Cargo.toml index 9f4327f31..b5dc5218b 100644 --- a/crates/stackable-versioned/Cargo.toml +++ b/crates/stackable-versioned/Cargo.toml @@ -14,7 +14,17 @@ all-features = true full = ["k8s"] k8s = [ "stackable-versioned-macros/k8s", # Forward the k8s feature to the underlying macro crate + "dep:k8s-version", + "dep:serde_json", + "dep:serde_yaml", + "dep:schemars", + "dep:serde", ] [dependencies] +k8s-version = { path = "../k8s-version", features = ["serde"], optional = true } stackable-versioned-macros = { path = "../stackable-versioned-macros" } +schemars = { workspace = true, optional = true } +serde = { workspace = true, optional = true } +serde_json = { workspace = true, optional = true } +serde_yaml = { workspace = true, optional = true } diff --git a/crates/stackable-versioned/src/lib.rs b/crates/stackable-versioned/src/lib.rs index 8c0c399b1..b324dbf5b 100644 --- a/crates/stackable-versioned/src/lib.rs +++ b/crates/stackable-versioned/src/lib.rs @@ -12,15 +12,43 @@ //! See [`versioned`] for an in-depth usage guide and a list of supported //! parameters. +use std::collections::HashMap; + +use k8s_version::Version; +use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec}; // Re-export macro pub use stackable_versioned_macros::*; -// Unused for now, might get picked up again in the future. -#[doc(hidden)] -pub trait AsVersionStr { - const VERSION: &'static str; +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct CrdValues { + /// List of values needed when downgrading to a particular version. + pub downgrades: HashMap>, + + /// List of values needed when upgrading to a particular version. + pub upgrades: HashMap>, +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct CrdValue { + /// The name of the field of the custom resource this value is for. + pub name: String, + + /// The value to be used when upgrading or downgrading the custom resource. + #[schemars(schema_with = "raw_object_schema")] + pub value: serde_yaml::Value, +} - fn as_version_str(&self) -> &'static str { - Self::VERSION - } +// TODO (@Techassi): Think about where this should live. Basically this already exists in +// stackable-operator, but we cannot use it without depending on it which I would like to +// avoid. +fn raw_object_schema(_: &mut schemars::r#gen::SchemaGenerator) -> Schema { + Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), + extensions: [( + "x-kubernetes-preserve-unknown-fields".to_owned(), + serde_json::Value::Bool(true), + )] + .into(), + ..Default::default() + }) }