Skip to content

Commit 231ec0b

Browse files
authored
feat(stackable-versioned): Use attribute instead of derive macro (#793)
* Update changelog * Add basic adjustements to support attribute macro * Add type alias for latest version * Remove unused code
1 parent 3548b01 commit 231ec0b

File tree

7 files changed

+66
-70
lines changed

7 files changed

+66
-70
lines changed

crates/stackable-versioned/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
- Change from derive macro to attribute macro to be able to generate code
8+
_in place_ instead of _appending_ new code ([#CHANGEME]).
79
- Improve action chain generation ([#784]).
810

9-
[#784](ttps://github.com/stackabletech/operator-rs/pull/784)
11+
[#784](https://github.com/stackabletech/operator-rs/pull/784)
12+
[#CHANGEME](https://github.com/stackabletech/operator-rs/pull/CHANGEME)
1013

1114
## [0.1.0] - 2024-05-08
1215

crates/stackable-versioned/src/attrs/container.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{cmp::Ordering, collections::HashSet, ops::Deref};
22

33
use darling::{
44
util::{Flag, SpannedValue},
5-
Error, FromDeriveInput, FromMeta, Result,
5+
Error, FromMeta, Result,
66
};
77
use k8s_version::Version;
88

@@ -12,13 +12,8 @@ use k8s_version::Version;
1212
///
1313
/// - `version`, which can occur one or more times. See [`VersionAttributes`].
1414
/// - `options`, which allow further customization of the generated code. See [`ContainerOptions`].
15-
#[derive(Clone, Debug, FromDeriveInput)]
16-
#[darling(
17-
attributes(versioned),
18-
supports(struct_named),
19-
forward_attrs(allow, doc, cfg, serde),
20-
and_then = ContainerAttributes::validate
21-
)]
15+
#[derive(Debug, FromMeta)]
16+
#[darling(and_then = ContainerAttributes::validate)]
2217
pub(crate) struct ContainerAttributes {
2318
#[darling(multiple, rename = "version")]
2419
pub(crate) versions: SpannedValue<Vec<VersionAttributes>>,

crates/stackable-versioned/src/gen/mod.rs

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
use darling::FromDeriveInput;
21
use proc_macro2::TokenStream;
32
use quote::ToTokens;
43
use syn::{spanned::Spanned, Data, DeriveInput, Error, Result};
54

65
use crate::{
76
attrs::container::ContainerAttributes,
8-
gen::{venum::VersionedEnum, version::ContainerVersion, vstruct::VersionedStruct},
7+
gen::{version::ContainerVersion, vstruct::VersionedStruct},
98
};
109

1110
pub(crate) mod field;
1211
pub(crate) mod neighbors;
13-
pub(crate) mod venum;
1412
pub(crate) mod version;
1513
pub(crate) mod vstruct;
1614

@@ -26,20 +24,13 @@ pub(crate) mod vstruct;
2624
// TODO (@Techassi): Think about how we can handle nested structs / enums which
2725
// are also versioned.
2826

29-
pub(crate) fn expand(input: DeriveInput) -> Result<TokenStream> {
30-
// Extract container attributes
31-
let attributes = ContainerAttributes::from_derive_input(&input)?;
32-
33-
// Validate container shape and generate code
27+
pub(crate) fn expand(attrs: ContainerAttributes, input: DeriveInput) -> Result<TokenStream> {
3428
let expanded = match input.data {
35-
Data::Struct(data) => {
36-
VersionedStruct::new(input.ident, data, attributes)?.to_token_stream()
37-
}
38-
Data::Enum(data) => VersionedEnum::new(input.ident, data, attributes)?.to_token_stream(),
39-
Data::Union(_) => {
29+
Data::Struct(data) => VersionedStruct::new(input.ident, data, attrs)?.to_token_stream(),
30+
_ => {
4031
return Err(Error::new(
4132
input.span(),
42-
"derive macro `Versioned` only supports structs and enums",
33+
"attribute macro `versioned` only supports structs",
4334
))
4435
}
4536
};

crates/stackable-versioned/src/gen/venum.rs

Lines changed: 0 additions & 23 deletions
This file was deleted.

crates/stackable-versioned/src/gen/vstruct.rs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ pub(crate) struct VersionedStruct {
2727
}
2828

2929
impl ToTokens for VersionedStruct {
30-
fn to_tokens(&self, _tokens: &mut TokenStream) {
31-
let mut versions = self.versions.iter().peekable();
30+
fn to_tokens(&self, tokens: &mut TokenStream) {
31+
let versions = self.versions.iter().peekable();
32+
let struct_name = &self.ident;
3233

33-
while let Some(version) = versions.next() {
34+
for version in versions {
3435
let mut fields = TokenStream::new();
3536

3637
for field in &self.fields {
@@ -43,24 +44,27 @@ impl ToTokens for VersionedStruct {
4344

4445
let deprecated_attr = version.deprecated.then_some(quote! {#[deprecated]});
4546
let module_name = format_ident!("{version}", version = version.inner.to_string());
46-
let struct_name = &self.ident;
47-
48-
// Only generate a module when there is at least one more version.
49-
// This skips generating a module for the latest version, because
50-
// the base struct always represents the latest version.
51-
if versions.peek().is_some() {
52-
_tokens.extend(quote! {
53-
#[automatically_derived]
54-
#deprecated_attr
55-
pub mod #module_name {
56-
57-
pub struct #struct_name {
58-
#fields
59-
}
47+
48+
tokens.extend(quote! {
49+
#[automatically_derived]
50+
#deprecated_attr
51+
pub mod #module_name {
52+
53+
pub struct #struct_name {
54+
#fields
6055
}
61-
});
62-
}
56+
}
57+
});
6358
}
59+
60+
// Special handling for the last (and thus latest) version
61+
let module_name = format_ident!(
62+
"{version}",
63+
version = self.versions.last().unwrap().inner.to_string()
64+
);
65+
tokens.extend(quote! {
66+
pub type #struct_name = #module_name::#struct_name;
67+
})
6468
}
6569
}
6670

crates/stackable-versioned/src/lib.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
1+
use darling::{ast::NestedMeta, FromMeta};
12
use proc_macro::TokenStream;
23
use syn::{DeriveInput, Error};
34

5+
use crate::attrs::container::ContainerAttributes;
6+
47
mod attrs;
58
mod consts;
69
mod gen;
710

8-
#[proc_macro_derive(Versioned, attributes(versioned))]
9-
pub fn versioned_macro_derive(input: TokenStream) -> TokenStream {
11+
#[proc_macro_attribute]
12+
pub fn versioned(attrs: TokenStream, input: TokenStream) -> TokenStream {
13+
let attrs = match NestedMeta::parse_meta_list(attrs.into()) {
14+
Ok(attrs) => match ContainerAttributes::from_list(&attrs) {
15+
Ok(attrs) => attrs,
16+
Err(err) => return err.write_errors().into(),
17+
},
18+
Err(err) => return darling::Error::from(err).write_errors().into(),
19+
};
20+
21+
// NOTE (@Techassi): For now, we can just use the DeriveInput type here,
22+
// because we only support structs (and eventually enums) to be versioned.
23+
// In the future - if we decide to support modules - this requires
24+
// adjustments to also support modules. One possible solution might be to
25+
// use an enum with two variants: Container(DeriveInput) and
26+
// Module(ItemMod).
1027
let input = syn::parse_macro_input!(input as DeriveInput);
1128

12-
gen::expand(input)
29+
gen::expand(attrs, input)
1330
.unwrap_or_else(Error::into_compile_error)
1431
.into()
1532
}

crates/stackable-versioned/tests/basic.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
use stackable_versioned::Versioned;
1+
use stackable_versioned::versioned;
22

33
// To expand the generated code (for debugging and testing), it is recommended
44
// to first change directory via `cd crates/stackable-versioned` and to then
55
// run `cargo expand --test basic --all-features`.
6-
#[derive(Versioned)]
76
#[allow(dead_code)]
87
#[versioned(
98
version(name = "v1alpha1"),
@@ -27,6 +26,16 @@ struct Foo {
2726
fn basic() {
2827
let _ = v1alpha1::Foo { jjj: 0, baz: false };
2928
let _ = v1beta1::Foo { bar: 0, baz: false };
29+
let _ = v1::Foo { bar: 0, baz: false };
30+
31+
#[allow(deprecated)]
32+
let _ = v2::Foo {
33+
deprecated_bar: 0,
34+
baz: false,
35+
};
36+
37+
// The latest version (v3)
38+
#[allow(deprecated)]
3039
let _ = Foo {
3140
deprecated_bar: 0,
3241
baz: false,

0 commit comments

Comments
 (0)