Skip to content

Commit abf5deb

Browse files
committed
refactor(stackable-versioned): Make conversion tracking opt in
1 parent 436e294 commit abf5deb

File tree

7 files changed

+257
-89
lines changed

7 files changed

+257
-89
lines changed

crates/stackable-versioned-macros/src/attrs/k8s.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,30 @@ use syn::Path;
2222
/// times.
2323
/// - `skip`: Controls skipping parts of the generation.
2424
#[derive(Clone, Debug, FromMeta)]
25-
pub(crate) struct KubernetesArguments {
26-
pub(crate) group: String,
27-
pub(crate) kind: Option<String>,
28-
pub(crate) singular: Option<String>,
29-
pub(crate) plural: Option<String>,
30-
pub(crate) namespaced: Flag,
25+
pub struct KubernetesArguments {
26+
pub group: String,
27+
pub kind: Option<String>,
28+
pub singular: Option<String>,
29+
pub plural: Option<String>,
30+
pub namespaced: Flag,
3131
// root
32-
pub(crate) crates: Option<KubernetesCrateArguments>,
33-
pub(crate) status: Option<Path>,
32+
pub crates: Option<KubernetesCrateArguments>,
33+
pub status: Option<Path>,
3434
// derive
3535
// schema
3636
// scale
3737
// printcolumn
3838
#[darling(multiple, rename = "shortname")]
39-
pub(crate) shortnames: Vec<String>,
39+
pub shortnames: Vec<String>,
4040
// category
4141
// selectable
4242
// doc
4343
// annotation
4444
// label
45-
pub(crate) skip: Option<KubernetesSkipArguments>,
45+
pub skip: Option<KubernetesSkipArguments>,
46+
47+
#[darling(default)]
48+
pub options: RawKubernetesOptions,
4649
}
4750

4851
/// This struct contains supported kubernetes skip arguments.
@@ -52,15 +55,15 @@ pub(crate) struct KubernetesArguments {
5255
/// - `merged_crd` flag, which skips generating the `crd()` and `merged_crd()` functions are
5356
/// generated.
5457
#[derive(Clone, Debug, FromMeta)]
55-
pub(crate) struct KubernetesSkipArguments {
58+
pub struct KubernetesSkipArguments {
5659
/// Whether the `crd()` and `merged_crd()` generation should be skipped for
5760
/// this container.
58-
pub(crate) merged_crd: Flag,
61+
pub merged_crd: Flag,
5962
}
6063

6164
/// This struct contains crate overrides to be passed to `#[kube]`.
6265
#[derive(Clone, Debug, FromMeta)]
63-
pub(crate) struct KubernetesCrateArguments {
66+
pub struct KubernetesCrateArguments {
6467
pub kube_core: Option<Path>,
6568
pub kube_client: Option<Path>,
6669
pub k8s_openapi: Option<Path>,
@@ -69,3 +72,8 @@ pub(crate) struct KubernetesCrateArguments {
6972
pub serde_json: Option<Path>,
7073
pub versioned: Option<Path>,
7174
}
75+
76+
#[derive(Clone, Default, Debug, FromMeta)]
77+
pub struct RawKubernetesOptions {
78+
pub experimental_conversion_tracking: Flag,
79+
}

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

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use syn::{Attribute, Ident, ItemEnum, ItemStruct, Path, Visibility, parse_quote}
88
use crate::{
99
attrs::{
1010
container::StandaloneContainerAttributes,
11-
k8s::{KubernetesArguments, KubernetesCrateArguments},
11+
k8s::{KubernetesArguments, KubernetesCrateArguments, RawKubernetesOptions},
1212
},
1313
codegen::{
1414
VersionDefinition,
@@ -44,13 +44,9 @@ pub enum Container {
4444

4545
impl Container {
4646
/// Generates the container definition for the specified `version`.
47-
pub(crate) fn generate_definition(
48-
&self,
49-
version: &VersionDefinition,
50-
multiple_versions: bool,
51-
) -> TokenStream {
47+
pub(crate) fn generate_definition(&self, version: &VersionDefinition) -> TokenStream {
5248
match self {
53-
Container::Struct(s) => s.generate_definition(version, multiple_versions),
49+
Container::Struct(s) => s.generate_definition(version),
5450
Container::Enum(e) => e.generate_definition(version),
5551
}
5652
}
@@ -206,12 +202,9 @@ impl StandaloneContainer {
206202
let mut kubernetes_enum_variant_strings = Vec::new();
207203

208204
let mut versions = self.versions.iter().peekable();
209-
let multiple_versions = versions.len() > 1;
210205

211206
while let Some(version) = versions.next() {
212-
let container_definition = self
213-
.container
214-
.generate_definition(version, multiple_versions);
207+
let container_definition = self.container.generate_definition(version);
215208

216209
// NOTE (@Techassi): Using '.copied()' here does not copy or clone the data, but instead
217210
// removes one level of indirection of the double reference '&&'.
@@ -266,9 +259,7 @@ impl StandaloneContainer {
266259
false,
267260
));
268261

269-
if multiple_versions {
270-
tokens.extend(self.container.generate_kubernetes_status_struct());
271-
}
262+
tokens.extend(self.container.generate_kubernetes_status_struct());
272263

273264
tokens
274265
}
@@ -314,6 +305,8 @@ pub struct ContainerOptions {
314305
pub skip_from: bool,
315306
}
316307

308+
// TODO (@Techassi): Get rid of this whole mess. There should be an elegant way of using the
309+
// attributes directly (with all defaults set and validation done).
317310
#[derive(Debug)]
318311
pub struct KubernetesOptions {
319312
pub group: String,
@@ -335,6 +328,7 @@ pub struct KubernetesOptions {
335328
// annotation
336329
// label
337330
pub skip_merged_crd: bool,
331+
pub config_options: KubernetesConfigOptions,
338332
}
339333

340334
impl From<KubernetesArguments> for KubernetesOptions {
@@ -351,6 +345,7 @@ impl From<KubernetesArguments> for KubernetesOptions {
351345
status: args.status,
352346
shortnames: args.shortnames,
353347
skip_merged_crd: args.skip.is_some_and(|s| s.merged_crd.is_present()),
348+
config_options: args.options.into(),
354349
}
355350
}
356351
}
@@ -473,3 +468,16 @@ impl<T> Deref for Override<T> {
473468
}
474469
}
475470
}
471+
472+
#[derive(Debug)]
473+
pub struct KubernetesConfigOptions {
474+
experimental_conversion_tracking: bool,
475+
}
476+
477+
impl From<RawKubernetesOptions> for KubernetesConfigOptions {
478+
fn from(options: RawKubernetesOptions) -> Self {
479+
Self {
480+
experimental_conversion_tracking: options.experimental_conversion_tracking.is_present(),
481+
}
482+
}
483+
}

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

Lines changed: 49 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,7 @@ pub struct Struct {
134134
// Common token generation
135135
impl Struct {
136136
/// Generates code for the struct definition.
137-
pub fn generate_definition(
138-
&self,
139-
version: &VersionDefinition,
140-
multiple_versions: bool,
141-
) -> TokenStream {
137+
pub fn generate_definition(&self, version: &VersionDefinition) -> TokenStream {
142138
let where_clause = self.generics.where_clause.as_ref();
143139
let type_generics = &self.generics;
144140

@@ -152,7 +148,7 @@ impl Struct {
152148
}
153149

154150
// This only returns Some, if K8s features are enabled
155-
let kube_attribute = self.generate_kube_attribute(version, multiple_versions);
151+
let kube_attribute = self.generate_kube_attribute(version);
156152

157153
quote! {
158154
#(#[doc = #version_docs])*
@@ -323,11 +319,7 @@ impl Struct {
323319
// makes keeping track of interconnected parts easier.
324320
// Kubernetes-specific token generation
325321
impl Struct {
326-
pub fn generate_kube_attribute(
327-
&self,
328-
version: &VersionDefinition,
329-
_multiple_versions: bool,
330-
) -> Option<TokenStream> {
322+
pub fn generate_kube_attribute(&self, version: &VersionDefinition) -> Option<TokenStream> {
331323
let kubernetes_options = self.common.options.kubernetes_options.as_ref()?;
332324

333325
// Required arguments
@@ -356,24 +348,16 @@ impl Struct {
356348
.then_some(quote! { , namespaced });
357349
let crates = kubernetes_options.crates.to_token_stream();
358350

359-
// TODO (@Techassi): Comment back in, once we are happy with the status struct
360-
// let status = if multiple_versions {
361-
// let status_ident = format_ident!(
362-
// "{struct_ident}Status",
363-
// struct_ident = self.common.idents.kubernetes.as_ident()
364-
// );
365-
// Some(quote! { , status = #status_ident })
366-
// } else {
367-
// kubernetes_options
368-
// .status
369-
// .as_ref()
370-
// .map(|s| quote! { , status = #s })
371-
// };
372-
373351
let status = kubernetes_options
374-
.status
375-
.as_ref()
376-
.map(|s| quote! { , status = #s });
352+
.config_options
353+
.experimental_conversion_tracking
354+
.then(|| {
355+
let status_ident = format_ident!(
356+
"{struct_ident}Status",
357+
struct_ident = self.common.idents.kubernetes.as_ident()
358+
);
359+
quote! { , status = #status_ident }
360+
});
377361

378362
let shortnames: TokenStream = kubernetes_options
379363
.shortnames
@@ -473,36 +457,42 @@ impl Struct {
473457
pub fn generate_kubernetes_status_struct(&self) -> Option<TokenStream> {
474458
let kubernetes_options = self.common.options.kubernetes_options.as_ref()?;
475459

476-
let status_ident = format_ident!(
477-
"{struct_ident}Status",
478-
struct_ident = self.common.idents.kubernetes.as_ident()
479-
);
480-
481-
let versioned_crate = &*kubernetes_options.crates.versioned;
482-
let schemars_crate = &*kubernetes_options.crates.schemars;
483-
let serde_crate = &*kubernetes_options.crates.serde;
484-
485-
let status = kubernetes_options.status.as_ref().map(|status| {
486-
quote! {
487-
#[serde(flatten)]
488-
pub status: #status,
489-
}
490-
});
491-
492-
Some(quote! {
493-
#[derive(
494-
::core::clone::Clone,
495-
::core::fmt::Debug,
496-
#serde_crate::Deserialize,
497-
#serde_crate::Serialize,
498-
#schemars_crate::JsonSchema
499-
)]
500-
#[serde(rename_all = "camelCase")]
501-
pub struct #status_ident {
502-
pub changed_values: #versioned_crate::ChangedValues,
503-
504-
#status
505-
}
506-
})
460+
kubernetes_options
461+
.config_options
462+
.experimental_conversion_tracking
463+
.then(|| {
464+
let status_ident = format_ident!(
465+
"{struct_ident}Status",
466+
struct_ident = self.common.idents.kubernetes.as_ident()
467+
);
468+
469+
let versioned_crate = &*kubernetes_options.crates.versioned;
470+
let schemars_crate = &*kubernetes_options.crates.schemars;
471+
let serde_crate = &*kubernetes_options.crates.serde;
472+
473+
// TODO (@Techassi): Validate that users don't specify the status we generate
474+
let status = kubernetes_options.status.as_ref().map(|status| {
475+
quote! {
476+
#[serde(flatten)]
477+
pub status: #status,
478+
}
479+
});
480+
481+
quote! {
482+
#[derive(
483+
::core::clone::Clone,
484+
::core::fmt::Debug,
485+
#serde_crate::Deserialize,
486+
#serde_crate::Serialize,
487+
#schemars_crate::JsonSchema
488+
)]
489+
#[serde(rename_all = "camelCase")]
490+
pub struct #status_ident {
491+
pub changed_values: #versioned_crate::ChangedValues,
492+
493+
#status
494+
}
495+
}
496+
})
507497
}
508498
}

crates/stackable-versioned-macros/src/codegen/module.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,6 @@ impl Module {
149149

150150
let mut kubernetes_container_items: HashMap<Ident, KubernetesItems> = HashMap::new();
151151
let mut versions = self.versions.iter().peekable();
152-
let multiple_versions = self.versions.len() > 1;
153152

154153
while let Some(version) = versions.next() {
155154
let next_version = versions.peek().copied();
@@ -159,8 +158,7 @@ impl Module {
159158
let version_ident = &version.ident;
160159

161160
for container in &self.containers {
162-
container_definitions
163-
.extend(container.generate_definition(version, multiple_versions));
161+
container_definitions.extend(container.generate_definition(version));
164162

165163
if !self.skip_from {
166164
from_impls.extend(container.generate_upgrade_from_impl(
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use kube::CustomResource;
2+
use schemars::JsonSchema;
3+
use serde::{Deserialize, Serialize};
4+
use stackable_versioned::versioned;
5+
// ---
6+
#[versioned(
7+
version(name = "v1alpha1"),
8+
version(name = "v1beta1"),
9+
version(name = "v1"),
10+
k8s(
11+
group = "stackable.tech",
12+
status = MyStatus,
13+
options(experimental_conversion_tracking),
14+
)
15+
)]
16+
// ---
17+
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)]
18+
pub(crate) struct FooSpec {
19+
#[versioned(added(since = "v1beta1"), changed(since = "v1", from_name = "bah"))]
20+
bar: usize,
21+
baz: bool,
22+
}
23+
// ---
24+
fn main() {}
25+
26+
#[derive(Clone, Debug, JsonSchema, Deserialize, Serialize)]
27+
pub struct MyStatus {
28+
foo: String,
29+
}
30+
31+
fn usize_to_u16(input: usize) -> u16 {
32+
input.try_into().unwrap()
33+
}

0 commit comments

Comments
 (0)