diff --git a/crates/stackable-versioned-macros/Cargo.toml b/crates/stackable-versioned-macros/Cargo.toml index 1f9030ab2..daf4ab885 100644 --- a/crates/stackable-versioned-macros/Cargo.toml +++ b/crates/stackable-versioned-macros/Cargo.toml @@ -15,7 +15,7 @@ all-features = true # are, however, used in K8s specific test cases. This is a false-positive and an # apparent limitation of cargo-udeps. These entries can be removed once # cargo-udeps supports detecting usage of such dependencies. -development = ["schemars", "serde_yaml", "stackable-versioned"] +development = ["k8s-openapi", "schemars", "serde_yaml", "stackable-versioned"] # cargo-udeps throws an error stating that these dependencies are unused. They are all marked as # optional, which trips up cargo-udeps for whatever reason... @@ -45,6 +45,7 @@ quote.workspace = true [dev-dependencies] # Only needed for doc tests / examples stackable-versioned = { path = "../stackable-versioned", features = ["k8s"] } +k8s-openapi = { workspace = true } insta.workspace = true prettyplease.workspace = true diff --git a/crates/stackable-versioned-macros/fixtures/inputs/default/enum_data_simple.rs b/crates/stackable-versioned-macros/fixtures/inputs/default/enum_data_simple.rs new file mode 100644 index 000000000..732bb0c2b --- /dev/null +++ b/crates/stackable-versioned-macros/fixtures/inputs/default/enum_data_simple.rs @@ -0,0 +1,12 @@ +#[versioned(version(name = "v1alpha1"), version(name = "v1alpha2"))] +// --- +enum Foo { + Foo, + Bar(u32, String), + + #[versioned(added(since = "v1alpha2"))] + Baz { + id: u32, + name: String, + }, +} 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 new file mode 100644 index 000000000..642b6560a --- /dev/null +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@enum_data_simple.rs.snap @@ -0,0 +1,31 @@ +--- +source: crates/stackable-versioned-macros/src/lib.rs +expression: formatted +input_file: crates/stackable-versioned-macros/fixtures/inputs/default/enum_data_simple.rs +--- +#[automatically_derived] +mod v1alpha1 { + use super::*; + pub enum Foo { + Foo, + Bar(u32, String), + } +} +#[automatically_derived] +impl From for v1alpha2::Foo { + fn from(__sv_foo: v1alpha1::Foo) -> Self { + match __sv_foo { + v1alpha1::Foo::Foo => v1alpha2::Foo::Foo, + v1alpha1::Foo::Bar(__sv_0, __sv_1) => v1alpha2::Foo::Bar(__sv_0, __sv_1), + } + } +} +#[automatically_derived] +mod v1alpha2 { + use super::*; + pub enum Foo { + Foo, + Bar(u32, String), + Baz { id: u32, name: String }, + } +} diff --git a/crates/stackable-versioned-macros/src/codegen/venum/variant.rs b/crates/stackable-versioned-macros/src/codegen/venum/variant.rs index 1415d6bb1..5377e3ba2 100644 --- a/crates/stackable-versioned-macros/src/codegen/venum/variant.rs +++ b/crates/stackable-versioned-macros/src/codegen/venum/variant.rs @@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut}; use darling::FromVariant; use proc_macro2::{Span, TokenStream}; -use quote::quote; +use quote::{format_ident, quote}; use syn::{token::Not, Ident, Type, TypeNever, Variant}; use crate::{ @@ -104,6 +104,7 @@ impl VersionedVariant { container_version: &ContainerVersion, ) -> Option { let original_attributes = &self.original_attributes; + let fields = &self.inner.fields; match &self.chain { // NOTE (@Techassi): https://rust-lang.github.io/rust-clippy/master/index.html#/expect_fun_call @@ -115,11 +116,11 @@ impl VersionedVariant { }) { ItemStatus::Addition { ident, .. } => Some(quote! { #(#original_attributes)* - #ident, + #ident #fields, }), ItemStatus::Change { to_ident, .. } => Some(quote! { #(#original_attributes)* - #to_ident, + #to_ident #fields, }), ItemStatus::Deprecation { ident, note, .. } => { // FIXME (@Techassi): Emitting the deprecated attribute @@ -139,7 +140,7 @@ impl VersionedVariant { Some(quote! { #(#original_attributes)* #deprecated_attr - #ident, + #ident #fields, }) } ItemStatus::NoChange { @@ -154,7 +155,7 @@ impl VersionedVariant { Some(quote! { #(#original_attributes)* #deprecated_attr - #ident, + #ident #fields, }) } ItemStatus::NotPresent => None, @@ -163,11 +164,11 @@ impl VersionedVariant { // If there is no chain of variant actions, the variant is not // versioned and code generation is straight forward. // Unversioned variants are always included in versioned enums. - let variant_ident = &self.inner.ident; + let ident = &self.inner.ident; Some(quote! { #(#original_attributes)* - #variant_ident, + #ident #fields, }) } } @@ -182,6 +183,38 @@ impl VersionedVariant { next_version: &ContainerVersion, enum_ident: &Ident, ) -> TokenStream { + let variant_data = match &self.inner.fields { + syn::Fields::Named(fields_named) => { + let field_names = fields_named + .named + .iter() + .map(|field| { + field + .ident + .as_ref() + .expect("named fields always have an ident") + .clone() + }) + .collect::>(); + + let tokens = quote! { { #(#field_names),* } }; + tokens + } + syn::Fields::Unnamed(fields_unnamed) => { + let field_names = fields_unnamed + .unnamed + .iter() + .enumerate() + .map(|(index, _)| format_ident!("__sv_{index}")) + .collect::>(); + + let tokens = quote! { ( #(#field_names),* ) }; + tokens + } + + syn::Fields::Unit => TokenStream::new(), + }; + match &self.chain { Some(chain) => match ( chain.get_expect(&version.inner), @@ -197,7 +230,7 @@ impl VersionedVariant { .expect("internal error: next variant must have a name"); quote! { - #module_name::#enum_ident::#old_variant_ident => #next_module_name::#enum_ident::#next_variant_ident, + #module_name::#enum_ident::#old_variant_ident #variant_data => #next_module_name::#enum_ident::#next_variant_ident #variant_data, } } }, @@ -205,7 +238,7 @@ impl VersionedVariant { let variant_ident = &self.inner.ident; quote! { - #module_name::#enum_ident::#variant_ident => #next_module_name::#enum_ident::#variant_ident, + #module_name::#enum_ident::#variant_ident #variant_data => #next_module_name::#enum_ident::#variant_ident #variant_data, } } } diff --git a/crates/stackable-versioned-macros/src/test_utils.rs b/crates/stackable-versioned-macros/src/test_utils.rs index 92dbca906..016fa9433 100644 --- a/crates/stackable-versioned-macros/src/test_utils.rs +++ b/crates/stackable-versioned-macros/src/test_utils.rs @@ -15,7 +15,7 @@ use crate::versioned_impl; const DELIMITER: &str = "// ---\n"; static REGEX: LazyLock = LazyLock::new(|| { - Regex::new(r"#\[versioned\(\n(?P[[:ascii:]]+)\n\)\]") + Regex::new(r"#\[versioned\(\s*(?P[[:ascii:]]+)\s*\)\]") .expect("failed to compile versioned regex") }); @@ -55,7 +55,7 @@ fn prepare_from_string(input: String) -> Result<(TokenStream, DeriveInput), Erro let attrs = REGEX .captures(attrs) - .unwrap() + .expect("the regex did not match") .name("args") .context(MissingRegexMatchGroupSnafu)? .as_str(); diff --git a/crates/stackable-versioned/CHANGELOG.md b/crates/stackable-versioned/CHANGELOG.md index e36236028..906df7922 100644 --- a/crates/stackable-versioned/CHANGELOG.md +++ b/crates/stackable-versioned/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- Add basic handling for enum variants with data ([#892]). + +### Fixed + +- Accept a wider variety of formatting styles in the macro testing regex ([#892]). + +[#892]: https://github.com/stackabletech/operator-rs/pull/892 + ## [0.4.0] - 2024-10-14 ### Added