Skip to content

Commit 64f3736

Browse files
authored
feat(stackable-versioned): Add support for generics (#969)
* chore: Add generics to Struct struct * feat: Add basic generics support for enums * feat: Add basic generics support for structs * test: Add snapshot tests for generics * chore: Add changelog entry
1 parent 15faafd commit 64f3736

9 files changed

+226
-10
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#[versioned(version(name = "v1alpha1"), version(name = "v1"))]
2+
// ---
3+
pub enum Foo<T>
4+
where
5+
T: Default,
6+
{
7+
Bar(T),
8+
Baz,
9+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#[versioned(
2+
version(name = "v1alpha1"),
3+
version(name = "v1"),
4+
options(preserve_module)
5+
)]
6+
// ---
7+
pub mod versioned {
8+
struct Foo<T>
9+
where
10+
T: Default,
11+
{
12+
bar: T,
13+
baz: u8,
14+
}
15+
16+
enum Boom<T>
17+
where
18+
T: Default,
19+
{
20+
Big(T),
21+
Shaq,
22+
}
23+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#[versioned(version(name = "v1alpha1"), version(name = "v1"))]
2+
// ---
3+
pub struct Foo<T>
4+
where
5+
T: Default,
6+
{
7+
bar: T,
8+
baz: u8,
9+
}

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

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 64 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/stackable-versioned-macros/src/codegen/container/enum.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::ops::Not;
33
use darling::{util::IdentString, FromAttributes, Result};
44
use proc_macro2::TokenStream;
55
use quote::quote;
6-
use syn::ItemEnum;
6+
use syn::{Generics, ItemEnum};
77

88
use crate::{
99
attrs::container::NestedContainerAttributes,
@@ -46,6 +46,7 @@ impl Container {
4646
};
4747

4848
Ok(Self::Enum(Enum {
49+
generics: item_enum.generics,
4950
variants: versioned_variants,
5051
common,
5152
}))
@@ -82,6 +83,7 @@ impl Container {
8283
};
8384

8485
Ok(Self::Enum(Enum {
86+
generics: item_enum.generics,
8587
variants: versioned_variants,
8688
common,
8789
}))
@@ -96,12 +98,16 @@ pub(crate) struct Enum {
9698

9799
/// Common container data which is shared between enums and structs.
98100
pub(crate) common: CommonContainerData,
101+
102+
/// Generic types of the enum
103+
pub generics: Generics,
99104
}
100105

101106
// Common token generation
102107
impl Enum {
103108
/// Generates code for the enum definition.
104109
pub(crate) fn generate_definition(&self, version: &VersionDefinition) -> TokenStream {
110+
let (_, type_generics, where_clause) = self.generics.split_for_impl();
105111
let original_attributes = &self.common.original_attributes;
106112
let ident = &self.common.idents.original;
107113
let version_docs = &version.docs;
@@ -114,7 +120,7 @@ impl Enum {
114120
quote! {
115121
#(#[doc = #version_docs])*
116122
#(#original_attributes)*
117-
pub enum #ident {
123+
pub enum #ident #type_generics #where_clause {
118124
#variants
119125
}
120126
}
@@ -133,6 +139,12 @@ impl Enum {
133139

134140
match next_version {
135141
Some(next_version) => {
142+
// TODO (@Techassi): Support generic types which have been removed in newer versions,
143+
// but need to exist for older versions How do we represent that? Because the
144+
// defined struct always represents the latest version. I guess we could generally
145+
// advise against using generic types, but if you have to, avoid removing it in
146+
// later versions.
147+
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
136148
let enum_ident = &self.common.idents.original;
137149
let from_ident = &self.common.idents.from;
138150

@@ -158,8 +170,10 @@ impl Enum {
158170
Some(quote! {
159171
#automatically_derived
160172
#allow_attribute
161-
impl ::std::convert::From<#version_ident::#enum_ident> for #next_version_ident::#enum_ident {
162-
fn from(#from_ident: #version_ident::#enum_ident) -> Self {
173+
impl #impl_generics ::std::convert::From<#version_ident::#enum_ident #type_generics> for #next_version_ident::#enum_ident #type_generics
174+
#where_clause
175+
{
176+
fn from(#from_ident: #version_ident::#enum_ident #type_generics) -> Self {
163177
match #from_ident {
164178
#variants
165179
}

crates/stackable-versioned-macros/src/codegen/container/struct.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::ops::Not;
33
use darling::{util::IdentString, Error, FromAttributes, Result};
44
use proc_macro2::TokenStream;
55
use quote::{quote, ToTokens};
6-
use syn::{parse_quote, ItemStruct, Path, Visibility};
6+
use syn::{parse_quote, Generics, ItemStruct, Path, Visibility};
77

88
use crate::{
99
attrs::container::NestedContainerAttributes,
@@ -58,6 +58,7 @@ impl Container {
5858
};
5959

6060
Ok(Self::Struct(Struct {
61+
generics: item_struct.generics,
6162
fields: versioned_fields,
6263
common,
6364
}))
@@ -113,6 +114,7 @@ impl Container {
113114
};
114115

115116
Ok(Self::Struct(Struct {
117+
generics: item_struct.generics,
116118
fields: versioned_fields,
117119
common,
118120
}))
@@ -123,16 +125,20 @@ impl Container {
123125
pub(crate) struct Struct {
124126
/// List of fields defined in the original struct. How, and if, an item
125127
/// should generate code, is decided by the currently generated version.
126-
pub(crate) fields: Vec<VersionedField>,
128+
pub fields: Vec<VersionedField>,
127129

128130
/// Common container data which is shared between structs and enums.
129-
pub(crate) common: CommonContainerData,
131+
pub common: CommonContainerData,
132+
133+
/// Generic types of the struct
134+
pub generics: Generics,
130135
}
131136

132137
// Common token generation
133138
impl Struct {
134139
/// Generates code for the struct definition.
135140
pub(crate) fn generate_definition(&self, version: &VersionDefinition) -> TokenStream {
141+
let (_, type_generics, where_clause) = self.generics.split_for_impl();
136142
let original_attributes = &self.common.original_attributes;
137143
let ident = &self.common.idents.original;
138144
let version_docs = &version.docs;
@@ -149,7 +155,7 @@ impl Struct {
149155
#(#[doc = #version_docs])*
150156
#(#original_attributes)*
151157
#kube_attribute
152-
pub struct #ident {
158+
pub struct #ident #type_generics #where_clause {
153159
#fields
154160
}
155161
}
@@ -168,6 +174,12 @@ impl Struct {
168174

169175
match next_version {
170176
Some(next_version) => {
177+
// TODO (@Techassi): Support generic types which have been removed in newer versions,
178+
// but need to exist for older versions How do we represent that? Because the
179+
// defined struct always represents the latest version. I guess we could generally
180+
// advise against using generic types, but if you have to, avoid removing it in
181+
// later versions.
182+
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
171183
let struct_ident = &self.common.idents.original;
172184
let from_struct_ident = &self.common.idents.from;
173185

@@ -194,8 +206,10 @@ impl Struct {
194206
Some(quote! {
195207
#automatically_derived
196208
#allow_attribute
197-
impl ::std::convert::From<#from_module_ident::#struct_ident> for #for_module_ident::#struct_ident {
198-
fn from(#from_struct_ident: #from_module_ident::#struct_ident) -> Self {
209+
impl #impl_generics ::std::convert::From<#from_module_ident::#struct_ident #type_generics> for #for_module_ident::#struct_ident #type_generics
210+
#where_clause
211+
{
212+
fn from(#from_struct_ident: #from_module_ident::#struct_ident #type_generics) -> Self {
199213
Self {
200214
#fields
201215
}

crates/stackable-versioned/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
### Added
8+
9+
- Add basic support for generic types in struct and enum definitions ([#969]).
10+
711
### Changed
812

913
- BREAKING: Move `preserve_module` option into `options` to unify option interface ([#961]).
1014

1115
[#961]: https://github.com/stackabletech/operator-rs/pull/961
16+
[#969]: https://github.com/stackabletech/operator-rs/pull/969
1217

1318
## [0.5.1] - 2025-02-14
1419

0 commit comments

Comments
 (0)