diff --git a/crates/stackable-versioned-macros/fixtures/inputs/default/basic_struct.rs b/crates/stackable-versioned-macros/fixtures/inputs/default/basic_struct.rs index 12ab3e260..2b0dae2df 100644 --- a/crates/stackable-versioned-macros/fixtures/inputs/default/basic_struct.rs +++ b/crates/stackable-versioned-macros/fixtures/inputs/default/basic_struct.rs @@ -7,6 +7,9 @@ )] // --- pub(crate) struct Foo { + #[versioned(added(since = "v1"))] + foo: String, + #[versioned( changed(since = "v1beta1", from_name = "jjj", from_type = "u8"), changed(since = "v1", from_type = "u16"), 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 c13ec224d..93dea7303 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 @@ -36,6 +36,7 @@ pub(crate) mod v1beta1 { impl ::std::convert::From for v1::Foo { fn from(__sv_foo: v1beta1::Foo) -> Self { Self { + foo: ::std::default::Default::default(), bar: __sv_foo.bar.into(), baz: __sv_foo.baz.into(), } @@ -45,6 +46,7 @@ impl ::std::convert::From for v1::Foo { pub(crate) mod v1 { use super::*; pub struct Foo { + pub foo: String, /// Test pub bar: usize, pub baz: bool, @@ -55,6 +57,7 @@ pub(crate) mod v1 { impl ::std::convert::From for v2::Foo { fn from(__sv_foo: v1::Foo) -> Self { Self { + foo: __sv_foo.foo.into(), deprecated_bar: __sv_foo.bar.into(), baz: __sv_foo.baz.into(), } @@ -64,6 +67,7 @@ impl ::std::convert::From for v2::Foo { pub(crate) mod v2 { use super::*; pub struct Foo { + pub foo: String, /// Test #[deprecated = "not empty"] pub deprecated_bar: usize, @@ -75,6 +79,7 @@ pub(crate) mod v2 { impl ::std::convert::From for v3::Foo { fn from(__sv_foo: v2::Foo) -> Self { Self { + foo: __sv_foo.foo.into(), deprecated_bar: __sv_foo.deprecated_bar.into(), baz: __sv_foo.baz.into(), } @@ -84,6 +89,7 @@ impl ::std::convert::From for v3::Foo { pub(crate) mod v3 { use super::*; pub struct Foo { + pub foo: String, /// Test #[deprecated] pub deprecated_bar: usize, diff --git a/crates/stackable-versioned-macros/src/codegen/changes.rs b/crates/stackable-versioned-macros/src/codegen/changes.rs index b7c5be052..545e42aa5 100644 --- a/crates/stackable-versioned-macros/src/codegen/changes.rs +++ b/crates/stackable-versioned-macros/src/codegen/changes.rs @@ -172,6 +172,10 @@ impl ChangesetExt for BTreeMap { ty, .. } => (ident, ty, *previously_deprecated), + ItemStatus::NotPresent => { + self.insert(version.inner, ItemStatus::NotPresent); + continue; + } // TODO (@NickLarsenNZ): Explain why it is unreachable, as it can be reached during testing. // To reproduce, use an invalid version, eg: #[versioned(deprecated(since = "v99"))] _ => unreachable!(), diff --git a/crates/stackable-versioned-macros/src/codegen/item/field.rs b/crates/stackable-versioned-macros/src/codegen/item/field.rs index 1dcda06cc..f55f774fa 100644 --- a/crates/stackable-versioned-macros/src/codegen/item/field.rs +++ b/crates/stackable-versioned-macros/src/codegen/item/field.rs @@ -143,6 +143,7 @@ impl VersionedField { } } + // TODO (@Techassi): This should probably return an optional token stream. pub(crate) fn generate_for_from_impl( &self, version: &VersionDefinition, @@ -155,6 +156,12 @@ impl VersionedField { 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 { diff --git a/crates/stackable-versioned-macros/tests/default/pass/added.rs b/crates/stackable-versioned-macros/tests/default/pass/added.rs new file mode 100644 index 000000000..0357b4877 --- /dev/null +++ b/crates/stackable-versioned-macros/tests/default/pass/added.rs @@ -0,0 +1,23 @@ +use stackable_versioned_macros::versioned; + +fn main() { + #[versioned( + version(name = "v1alpha1"), + version(name = "v1alpha2"), + version(name = "v1beta1"), + version(name = "v1") + )] + struct Foo { + username: String, + + #[versioned(added(since = "v1alpha2", default = default_first_name))] + first_name: String, + + #[versioned(added(since = "v1beta1"))] + last_name: String, + } +} + +fn default_first_name() -> String { + "foo".into() +} diff --git a/crates/stackable-versioned-macros/tests/trybuild.rs b/crates/stackable-versioned-macros/tests/trybuild.rs index 3070189c9..dbe5f1d63 100644 --- a/crates/stackable-versioned-macros/tests/trybuild.rs +++ b/crates/stackable-versioned-macros/tests/trybuild.rs @@ -27,6 +27,7 @@ mod default { fn default_macros() { let t = trybuild::TestCases::new(); t.compile_fail("tests/default/fail/*.rs"); + t.pass("tests/default/pass/*.rs"); } #[cfg(feature = "k8s")] diff --git a/crates/stackable-versioned/CHANGELOG.md b/crates/stackable-versioned/CHANGELOG.md index b3bda2e07..9fee8c70c 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] +### Fixed + +- Correctly handle fields added in later versions ([#1031]). + +[#1031]: https://github.com/stackabletech/operator-rs/pull/1031 + ## [0.7.1] - 2025-04-02 ### Fixed