Skip to content

Commit df262a3

Browse files
committed
chore: Merge branch 'main' into feat/crd-versioning-forward-k8s-args
2 parents a8a982d + 9f774d1 commit df262a3

File tree

4 files changed

+109
-32
lines changed

4 files changed

+109
-32
lines changed

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

+12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::ops::Deref;
22

3+
use convert_case::{Case, Casing};
4+
use k8s_version::Version;
35
use proc_macro2::TokenStream;
46
use quote::format_ident;
57
use syn::{Attribute, Ident, Visibility};
@@ -52,6 +54,16 @@ impl IdentExt for Ident {
5254
}
5355
}
5456

57+
pub(crate) trait VersionExt {
58+
fn as_variant_ident(&self) -> Ident;
59+
}
60+
61+
impl VersionExt for Version {
62+
fn as_variant_ident(&self) -> Ident {
63+
format_ident!("{ident}", ident = self.to_string().to_case(Case::Pascal))
64+
}
65+
}
66+
5567
/// This struct bundles values from [`DeriveInput`][1].
5668
///
5769
/// [`DeriveInput`][1] cannot be used directly when constructing a

crates/stackable-versioned-macros/src/codegen/vstruct/mod.rs

+88-31
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ use syn::{parse_quote, DataStruct, Error, Ident};
88
use crate::{
99
attrs::common::ContainerAttributes,
1010
codegen::{
11-
common::{Container, ContainerInput, ContainerVersion, Item, VersionedContainer},
11+
common::{
12+
Container, ContainerInput, ContainerVersion, Item, VersionExt, VersionedContainer,
13+
},
1214
vstruct::field::VersionedField,
1315
},
1416
};
1517

1618
pub(crate) mod field;
1719

20+
type GenerateVersionReturn = (TokenStream, Option<(TokenStream, (Ident, String))>);
21+
1822
/// Stores individual versions of a single struct. Each version tracks field
1923
/// actions, which describe if the field was added, renamed or deprecated in
2024
/// that version. Fields which are not versioned, are included in every
@@ -85,24 +89,30 @@ impl Container<DataStruct, VersionedField> for VersionedStruct {
8589
}
8690

8791
fn generate_tokens(&self) -> TokenStream {
88-
let mut kubernetes_crd_fn_calls = TokenStream::new();
89-
let mut container_definition = TokenStream::new();
92+
let mut tokens = TokenStream::new();
93+
94+
let mut enum_variants = Vec::new();
95+
let mut crd_fn_calls = Vec::new();
9096

9197
let mut versions = self.versions.iter().peekable();
9298

9399
while let Some(version) = versions.next() {
94-
container_definition.extend(self.generate_version(version, versions.peek().copied()));
95-
kubernetes_crd_fn_calls.extend(self.generate_kubernetes_crd_fn_call(version));
100+
let (container_definition, merged_crd) =
101+
self.generate_version(version, versions.peek().copied());
102+
103+
if let Some((crd_fn_call, enum_variant)) = merged_crd {
104+
enum_variants.push(enum_variant);
105+
crd_fn_calls.push(crd_fn_call);
106+
}
107+
108+
tokens.extend(container_definition);
96109
}
97110

98-
// If tokens for the 'crd()' function calls were generated, also generate
99-
// the 'merge_crds' call.
100-
if !kubernetes_crd_fn_calls.is_empty() {
101-
container_definition
102-
.extend(self.generate_kubernetes_merge_crds(kubernetes_crd_fn_calls));
111+
if !crd_fn_calls.is_empty() {
112+
tokens.extend(self.generate_kubernetes_merge_crds(crd_fn_calls, enum_variants));
103113
}
104114

105-
container_definition
115+
tokens
106116
}
107117
}
108118

@@ -112,7 +122,7 @@ impl VersionedStruct {
112122
&self,
113123
version: &ContainerVersion,
114124
next_version: Option<&ContainerVersion>,
115-
) -> TokenStream {
125+
) -> GenerateVersionReturn {
116126
let mut token_stream = TokenStream::new();
117127

118128
let original_attributes = &self.original_attributes;
@@ -137,7 +147,27 @@ impl VersionedStruct {
137147
let version_specific_docs = self.generate_struct_docs(version);
138148

139149
// Generate K8s specific code
140-
let kubernetes_cr_derive = self.generate_kubernetes_cr_derive(version);
150+
let (kubernetes_cr_derive, merged_crd) = match &self.options.kubernetes_options {
151+
Some(options) => {
152+
// Generate the CustomResource derive macro with the appropriate
153+
// attributes supplied using #[kube()].
154+
let cr_derive = self.generate_kubernetes_cr_derive(version);
155+
156+
// Generate merged_crd specific code when not opted out.
157+
let merged_crd = if !options.skip_merged_crd {
158+
let crd_fn_call = self.generate_kubernetes_crd_fn_call(version);
159+
let enum_variant = version.inner.as_variant_ident();
160+
let enum_display = version.inner.to_string();
161+
162+
Some((crd_fn_call, (enum_variant, enum_display)))
163+
} else {
164+
None
165+
};
166+
167+
(Some(cr_derive), merged_crd)
168+
}
169+
None => (None, None),
170+
};
141171

142172
// Generate tokens for the module and the contained struct
143173
token_stream.extend(quote! {
@@ -160,7 +190,7 @@ impl VersionedStruct {
160190
token_stream.extend(self.generate_from_impl(version, next_version));
161191
}
162192

163-
token_stream
193+
(token_stream, merged_crd)
164194
}
165195

166196
/// Generates version specific doc comments for the struct.
@@ -284,44 +314,71 @@ impl VersionedStruct {
284314
}
285315

286316
/// Generates the `merge_crds` function call.
287-
fn generate_kubernetes_merge_crds(&self, fn_calls: TokenStream) -> TokenStream {
317+
fn generate_kubernetes_merge_crds(
318+
&self,
319+
crd_fn_calls: Vec<TokenStream>,
320+
enum_variants: Vec<(Ident, String)>,
321+
) -> TokenStream {
288322
let ident = &self.idents.kubernetes;
289323

324+
let version_enum_definition = self.generate_kubernetes_version_enum(enum_variants);
325+
290326
quote! {
291327
#[automatically_derived]
292328
pub struct #ident;
293329

330+
#version_enum_definition
331+
294332
#[automatically_derived]
295333
impl #ident {
296334
/// Generates a merged CRD which contains all versions defined using the
297335
/// `#[versioned()]` macro.
298336
pub fn merged_crd(
299-
stored_apiversion: &str
337+
stored_apiversion: Version
300338
) -> ::std::result::Result<::k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition, ::kube::core::crd::MergeError> {
301-
::kube::core::crd::merge_crds(vec![#fn_calls], stored_apiversion)
339+
::kube::core::crd::merge_crds(vec![#(#crd_fn_calls),*], &stored_apiversion.to_string())
302340
}
303341
}
304342
}
305343
}
306344

307345
/// Generates the inner `crd()` functions calls which get used in the
308346
/// `merge_crds` function.
309-
fn generate_kubernetes_crd_fn_call(&self, version: &ContainerVersion) -> Option<TokenStream> {
310-
if self
311-
.options
312-
.kubernetes_options
313-
.as_ref()
314-
.is_some_and(|o| !o.skip_merged_crd)
315-
{
316-
let struct_ident = &self.idents.kubernetes;
317-
let version_ident = &version.ident;
318-
319-
let path: syn::Path = parse_quote!(#version_ident::#struct_ident);
320-
return Some(quote! {
321-
<#path as ::kube::CustomResourceExt>::crd(),
347+
fn generate_kubernetes_crd_fn_call(&self, version: &ContainerVersion) -> TokenStream {
348+
let struct_ident = &self.idents.kubernetes;
349+
let version_ident = &version.ident;
350+
let path: syn::Path = parse_quote!(#version_ident::#struct_ident);
351+
352+
quote! {
353+
<#path as ::kube::CustomResourceExt>::crd()
354+
}
355+
}
356+
357+
fn generate_kubernetes_version_enum(&self, enum_variants: Vec<(Ident, String)>) -> TokenStream {
358+
let mut enum_variant_matches = TokenStream::new();
359+
let mut enum_variant_idents = TokenStream::new();
360+
361+
for (enum_variant_ident, enum_variant_display) in enum_variants {
362+
enum_variant_idents.extend(quote! {#enum_variant_ident,});
363+
enum_variant_matches.extend(quote! {
364+
Version::#enum_variant_ident => f.write_str(#enum_variant_display),
322365
});
323366
}
324367

325-
None
368+
quote! {
369+
#[automatically_derived]
370+
pub enum Version {
371+
#enum_variant_idents
372+
}
373+
374+
#[automatically_derived]
375+
impl ::std::fmt::Display for Version {
376+
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::result::Result<(), ::std::fmt::Error> {
377+
match self {
378+
#enum_variant_matches
379+
}
380+
}
381+
}
382+
}
326383
}
327384
}

crates/stackable-versioned-macros/tests/k8s/pass/crd.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ fn main() {
2626
baz: bool,
2727
}
2828

29-
let merged_crd = Foo::merged_crd("v1").unwrap();
29+
let merged_crd = Foo::merged_crd(Version::V1).unwrap();
3030
println!("{}", serde_yaml::to_string(&merged_crd).unwrap());
3131
}

crates/stackable-versioned/CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@ All notable changes to this project will be documented in this file.
88

99
- Add forwarding of `singular`, `plural`, and `namespaced` arguments in `k8s()`
1010
([#873]).
11+
- Generate a `Version` enum containing all declared versions as variants
12+
([#872]).
1113

14+
### Changed
15+
16+
- The `merged_crd` associated function now takes `Version` instead of `&str` as
17+
input ([#872]).
18+
19+
[#872]: https://github.com/stackabletech/operator-rs/pull/872
1220
[#873]: https://github.com/stackabletech/operator-rs/pull/873
1321

1422
## [0.2.0] - 2024-09-19

0 commit comments

Comments
 (0)