Skip to content

Commit 244eda7

Browse files
authored
feat(stackable-versioned): Add basic handling for enum variants with data (#892)
* fix(stackable-versioned): allow various formatting styles for macro in testing regex * fix(stackable-versioned): add k8s-openapi as a dev-dependency * feat(stackable-versioned): basic handling for enum variants with data * chore(stackable-versioned): Update changelog * chore(stackable-versioned): Make udeps happy with k8s-openapi
1 parent cb4218b commit 244eda7

File tree

6 files changed

+99
-12
lines changed

6 files changed

+99
-12
lines changed

crates/stackable-versioned-macros/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ all-features = true
1515
# are, however, used in K8s specific test cases. This is a false-positive and an
1616
# apparent limitation of cargo-udeps. These entries can be removed once
1717
# cargo-udeps supports detecting usage of such dependencies.
18-
development = ["schemars", "serde_yaml", "stackable-versioned"]
18+
development = ["k8s-openapi", "schemars", "serde_yaml", "stackable-versioned"]
1919

2020
# cargo-udeps throws an error stating that these dependencies are unused. They are all marked as
2121
# optional, which trips up cargo-udeps for whatever reason...
@@ -45,6 +45,7 @@ quote.workspace = true
4545
[dev-dependencies]
4646
# Only needed for doc tests / examples
4747
stackable-versioned = { path = "../stackable-versioned", features = ["k8s"] }
48+
k8s-openapi = { workspace = true }
4849

4950
insta.workspace = true
5051
prettyplease.workspace = true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#[versioned(version(name = "v1alpha1"), version(name = "v1alpha2"))]
2+
// ---
3+
enum Foo {
4+
Foo,
5+
Bar(u32, String),
6+
7+
#[versioned(added(since = "v1alpha2"))]
8+
Baz {
9+
id: u32,
10+
name: String,
11+
},
12+
}

crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@enum_data_simple.rs.snap

+31
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/stackable-versioned-macros/src/codegen/venum/variant.rs

+42-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut};
22

33
use darling::FromVariant;
44
use proc_macro2::{Span, TokenStream};
5-
use quote::quote;
5+
use quote::{format_ident, quote};
66
use syn::{token::Not, Ident, Type, TypeNever, Variant};
77

88
use crate::{
@@ -104,6 +104,7 @@ impl VersionedVariant {
104104
container_version: &ContainerVersion,
105105
) -> Option<TokenStream> {
106106
let original_attributes = &self.original_attributes;
107+
let fields = &self.inner.fields;
107108

108109
match &self.chain {
109110
// NOTE (@Techassi): https://rust-lang.github.io/rust-clippy/master/index.html#/expect_fun_call
@@ -115,11 +116,11 @@ impl VersionedVariant {
115116
}) {
116117
ItemStatus::Addition { ident, .. } => Some(quote! {
117118
#(#original_attributes)*
118-
#ident,
119+
#ident #fields,
119120
}),
120121
ItemStatus::Change { to_ident, .. } => Some(quote! {
121122
#(#original_attributes)*
122-
#to_ident,
123+
#to_ident #fields,
123124
}),
124125
ItemStatus::Deprecation { ident, note, .. } => {
125126
// FIXME (@Techassi): Emitting the deprecated attribute
@@ -139,7 +140,7 @@ impl VersionedVariant {
139140
Some(quote! {
140141
#(#original_attributes)*
141142
#deprecated_attr
142-
#ident,
143+
#ident #fields,
143144
})
144145
}
145146
ItemStatus::NoChange {
@@ -154,7 +155,7 @@ impl VersionedVariant {
154155
Some(quote! {
155156
#(#original_attributes)*
156157
#deprecated_attr
157-
#ident,
158+
#ident #fields,
158159
})
159160
}
160161
ItemStatus::NotPresent => None,
@@ -163,11 +164,11 @@ impl VersionedVariant {
163164
// If there is no chain of variant actions, the variant is not
164165
// versioned and code generation is straight forward.
165166
// Unversioned variants are always included in versioned enums.
166-
let variant_ident = &self.inner.ident;
167+
let ident = &self.inner.ident;
167168

168169
Some(quote! {
169170
#(#original_attributes)*
170-
#variant_ident,
171+
#ident #fields,
171172
})
172173
}
173174
}
@@ -182,6 +183,38 @@ impl VersionedVariant {
182183
next_version: &ContainerVersion,
183184
enum_ident: &Ident,
184185
) -> TokenStream {
186+
let variant_data = match &self.inner.fields {
187+
syn::Fields::Named(fields_named) => {
188+
let field_names = fields_named
189+
.named
190+
.iter()
191+
.map(|field| {
192+
field
193+
.ident
194+
.as_ref()
195+
.expect("named fields always have an ident")
196+
.clone()
197+
})
198+
.collect::<Vec<_>>();
199+
200+
let tokens = quote! { { #(#field_names),* } };
201+
tokens
202+
}
203+
syn::Fields::Unnamed(fields_unnamed) => {
204+
let field_names = fields_unnamed
205+
.unnamed
206+
.iter()
207+
.enumerate()
208+
.map(|(index, _)| format_ident!("__sv_{index}"))
209+
.collect::<Vec<_>>();
210+
211+
let tokens = quote! { ( #(#field_names),* ) };
212+
tokens
213+
}
214+
215+
syn::Fields::Unit => TokenStream::new(),
216+
};
217+
185218
match &self.chain {
186219
Some(chain) => match (
187220
chain.get_expect(&version.inner),
@@ -197,15 +230,15 @@ impl VersionedVariant {
197230
.expect("internal error: next variant must have a name");
198231

199232
quote! {
200-
#module_name::#enum_ident::#old_variant_ident => #next_module_name::#enum_ident::#next_variant_ident,
233+
#module_name::#enum_ident::#old_variant_ident #variant_data => #next_module_name::#enum_ident::#next_variant_ident #variant_data,
201234
}
202235
}
203236
},
204237
None => {
205238
let variant_ident = &self.inner.ident;
206239

207240
quote! {
208-
#module_name::#enum_ident::#variant_ident => #next_module_name::#enum_ident::#variant_ident,
241+
#module_name::#enum_ident::#variant_ident #variant_data => #next_module_name::#enum_ident::#variant_ident #variant_data,
209242
}
210243
}
211244
}

crates/stackable-versioned-macros/src/test_utils.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::versioned_impl;
1515
const DELIMITER: &str = "// ---\n";
1616

1717
static REGEX: LazyLock<Regex> = LazyLock::new(|| {
18-
Regex::new(r"#\[versioned\(\n(?P<args>[[:ascii:]]+)\n\)\]")
18+
Regex::new(r"#\[versioned\(\s*(?P<args>[[:ascii:]]+)\s*\)\]")
1919
.expect("failed to compile versioned regex")
2020
});
2121

@@ -55,7 +55,7 @@ fn prepare_from_string(input: String) -> Result<(TokenStream, DeriveInput), Erro
5555

5656
let attrs = REGEX
5757
.captures(attrs)
58-
.unwrap()
58+
.expect("the regex did not match")
5959
.name("args")
6060
.context(MissingRegexMatchGroupSnafu)?
6161
.as_str();

crates/stackable-versioned/CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
### Added
8+
9+
- Add basic handling for enum variants with data ([#892]).
10+
11+
### Fixed
12+
13+
- Accept a wider variety of formatting styles in the macro testing regex ([#892]).
14+
15+
[#892]: https://github.com/stackabletech/operator-rs/pull/892
16+
717
## [0.4.0] - 2024-10-14
818

919
### Added

0 commit comments

Comments
 (0)