Skip to content

Commit bcced0c

Browse files
committed
feat(stackable-versioned): Generate downgrade From impls
1 parent dddd78f commit bcced0c

File tree

8 files changed

+288
-108
lines changed

8 files changed

+288
-108
lines changed

crates/stackable-versioned-macros/src/attrs/item/mod.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,13 @@ impl CommonItemAttributes {
240240

241241
// The convert_with argument only makes sense to use when the
242242
// type changed
243-
if let Some(convert_func) = change.convert_with.as_ref() {
243+
if let Some(upgrade_func) = change.upgrade_with.as_ref() {
244244
if change.from_type.is_none() {
245245
errors.push(
246246
Error::custom(
247247
"the `convert_with` argument must be used in combination with `from_type`",
248248
)
249-
.with_span(&convert_func.span()),
249+
.with_span(&upgrade_func.span()),
250250
);
251251
}
252252
}
@@ -315,7 +315,8 @@ impl CommonItemAttributes {
315315
.unwrap_or(ty.clone());
316316

317317
actions.insert(*change.since, ItemStatus::Change {
318-
convert_with: change.convert_with.as_deref().cloned(),
318+
downgrade_with: change.downgrade_with.as_deref().cloned(),
319+
upgrade_with: change.upgrade_with.as_deref().cloned(),
319320
from_ident: from_ident.clone(),
320321
from_type: from_ty.clone(),
321322
to_ident: ident,
@@ -359,7 +360,8 @@ impl CommonItemAttributes {
359360
.unwrap_or(ty.clone());
360361

361362
actions.insert(*change.since, ItemStatus::Change {
362-
convert_with: change.convert_with.as_deref().cloned(),
363+
downgrade_with: change.downgrade_with.as_deref().cloned(),
364+
upgrade_with: change.upgrade_with.as_deref().cloned(),
363365
from_ident: from_ident.clone(),
364366
from_type: from_ty.clone(),
365367
to_ident: ident,
@@ -435,7 +437,8 @@ pub struct ChangedAttributes {
435437
pub since: SpannedValue<Version>,
436438
pub from_name: Option<SpannedValue<String>>,
437439
pub from_type: Option<SpannedValue<Type>>,
438-
pub convert_with: Option<SpannedValue<Path>>,
440+
pub upgrade_with: Option<SpannedValue<Path>>,
441+
pub downgrade_with: Option<SpannedValue<Path>>,
439442
}
440443

441444
/// For the deprecated() action

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

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::ops::Not;
22

3-
use darling::{FromAttributes, Result, util::IdentString};
3+
use darling::{FromAttributes, Result};
44
use proc_macro2::TokenStream;
55
use quote::quote;
66
use syn::{Generics, ItemEnum};
@@ -126,11 +126,11 @@ impl Enum {
126126
}
127127

128128
/// Generates code for the `From<Version> for NextVersion` implementation.
129-
pub(crate) fn generate_from_impl(
129+
pub fn generate_upgrade_from_impl(
130130
&self,
131131
version: &VersionDefinition,
132132
next_version: Option<&VersionDefinition>,
133-
is_nested: bool,
133+
add_attributes: bool,
134134
) -> Option<TokenStream> {
135135
if version.skip_from || self.common.options.skip_from {
136136
return None;
@@ -145,12 +145,17 @@ impl Enum {
145145
// later versions.
146146
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
147147
let enum_ident = &self.common.idents.original;
148-
let from_ident = &self.common.idents.from;
148+
let from_enum_ident = &self.common.idents.from;
149149

150-
let next_version_ident = &next_version.ident;
151-
let version_ident = &version.ident;
150+
let for_module_ident = &next_version.ident;
151+
let from_module_ident = &version.ident;
152152

153-
let variants = self.generate_from_variants(version, next_version, enum_ident);
153+
let variants: TokenStream = self
154+
.variants
155+
.iter()
156+
.map(|v| v.generate_for_upgrade_from_impl(version, next_version, enum_ident))
157+
.flatten()
158+
.collect();
154159

155160
// Include allow(deprecated) only when this or the next version is
156161
// deprecated. Also include it, when a variant in this or the next
@@ -163,17 +168,18 @@ impl Enum {
163168

164169
// Only add the #[automatically_derived] attribute only if this impl is used
165170
// outside of a module (in standalone mode).
166-
let automatically_derived =
167-
is_nested.not().then(|| quote! {#[automatically_derived]});
171+
let automatically_derived = add_attributes
172+
.not()
173+
.then(|| quote! {#[automatically_derived]});
168174

169175
Some(quote! {
170176
#automatically_derived
171177
#allow_attribute
172-
impl #impl_generics ::std::convert::From<#version_ident::#enum_ident #type_generics> for #next_version_ident::#enum_ident #type_generics
178+
impl #impl_generics ::std::convert::From<#from_module_ident::#enum_ident #type_generics> for #for_module_ident::#enum_ident #type_generics
173179
#where_clause
174180
{
175-
fn from(#from_ident: #version_ident::#enum_ident #type_generics) -> Self {
176-
match #from_ident {
181+
fn from(#from_enum_ident: #from_module_ident::#enum_ident #type_generics) -> Self {
182+
match #from_enum_ident {
177183
#variants
178184
}
179185
}
@@ -184,20 +190,53 @@ impl Enum {
184190
}
185191
}
186192

187-
/// Generates code for enum variants used in `From` implementations.
188-
fn generate_from_variants(
193+
pub fn generate_downgrade_from_impl(
189194
&self,
190195
version: &VersionDefinition,
191-
next_version: &VersionDefinition,
192-
enum_ident: &IdentString,
193-
) -> TokenStream {
194-
let mut tokens = TokenStream::new();
195-
196-
for variant in &self.variants {
197-
tokens.extend(variant.generate_for_from_impl(version, next_version, enum_ident));
196+
next_version: Option<&VersionDefinition>,
197+
add_attributes: bool,
198+
) -> Option<TokenStream> {
199+
if version.skip_from || self.common.options.skip_from {
200+
return None;
198201
}
199202

200-
tokens
203+
match next_version {
204+
Some(next_version) => {
205+
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
206+
let enum_ident = &self.common.idents.original;
207+
let from_enum_ident = &self.common.idents.from;
208+
209+
let for_module_ident = &version.ident;
210+
let from_module_ident = &next_version.ident;
211+
212+
let variants: TokenStream = self
213+
.variants
214+
.iter()
215+
.map(|v| v.generate_for_downgrade_from_impl(version, next_version, enum_ident))
216+
.flatten()
217+
.collect();
218+
219+
// Only add the #[automatically_derived] attribute only if this impl is used
220+
// outside of a module (in standalone mode).
221+
let automatically_derived = add_attributes
222+
.not()
223+
.then(|| quote! {#[automatically_derived]});
224+
225+
Some(quote! {
226+
#automatically_derived
227+
impl #impl_generics ::std::convert::From<#from_module_ident::#enum_ident #type_generics> for #for_module_ident::#enum_ident #type_generics
228+
#where_clause
229+
{
230+
fn from(#from_enum_ident: #from_module_ident::#enum_ident #type_generics) -> Self {
231+
match #from_enum_ident {
232+
#variants
233+
}
234+
}
235+
}
236+
})
237+
}
238+
None => None,
239+
}
201240
}
202241

203242
/// Returns whether any variant is deprecated in the provided `version`.

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

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ mod r#enum;
2121
mod r#struct;
2222

2323
/// Contains common container data shared between structs and enums.
24-
pub(crate) struct CommonContainerData {
24+
pub struct CommonContainerData {
2525
/// Original attributes placed on the container, like `#[derive()]` or `#[cfg()]`.
2626
pub(crate) original_attributes: Vec<Attribute>,
2727

@@ -37,7 +37,7 @@ pub(crate) struct CommonContainerData {
3737
/// Abstracting away with kind of container is generated makes it possible to create a list of
3838
/// containers when the macro is used on modules. This enum provides functions to generate code
3939
/// which then internally call the appropriate function based on the variant.
40-
pub(crate) enum Container {
40+
pub enum Container {
4141
Struct(Struct),
4242
Enum(Enum),
4343
}
@@ -51,16 +51,37 @@ impl Container {
5151
}
5252
}
5353

54-
/// Generates the container `From<Version> for NextVersion` implementation.
55-
pub(crate) fn generate_from_impl(
54+
/// Generates the `From<Version> for NextVersion` implementation for the container.
55+
pub fn generate_upgrade_from_impl(
5656
&self,
5757
version: &VersionDefinition,
5858
next_version: Option<&VersionDefinition>,
5959
add_attributes: bool,
6060
) -> Option<TokenStream> {
6161
match self {
62-
Container::Struct(s) => s.generate_from_impl(version, next_version, add_attributes),
63-
Container::Enum(e) => e.generate_from_impl(version, next_version, add_attributes),
62+
Container::Struct(s) => {
63+
s.generate_upgrade_from_impl(version, next_version, add_attributes)
64+
}
65+
Container::Enum(e) => {
66+
e.generate_upgrade_from_impl(version, next_version, add_attributes)
67+
}
68+
}
69+
}
70+
71+
/// Generates the `From<NextVersion> for Version` implementation for the container.
72+
pub fn generate_downgrade_from_impl(
73+
&self,
74+
version: &VersionDefinition,
75+
next_version: Option<&VersionDefinition>,
76+
add_attributes: bool,
77+
) -> Option<TokenStream> {
78+
match self {
79+
Container::Struct(s) => {
80+
s.generate_downgrade_from_impl(version, next_version, add_attributes)
81+
}
82+
Container::Enum(e) => {
83+
e.generate_downgrade_from_impl(version, next_version, add_attributes)
84+
}
6485
}
6586
}
6687

@@ -180,9 +201,17 @@ impl StandaloneContainer {
180201

181202
// NOTE (@Techassi): Using '.copied()' here does not copy or clone the data, but instead
182203
// removes one level of indirection of the double reference '&&'.
183-
let from_impl =
204+
let next_version = versions.peek().copied();
205+
206+
// Generate the From impl for upgrading the CRD.
207+
let upgrade_from_impl =
208+
self.container
209+
.generate_upgrade_from_impl(version, next_version, false);
210+
211+
// Generate the From impl for downgrading the CRD.
212+
let downgrade_from_impl =
184213
self.container
185-
.generate_from_impl(version, versions.peek().copied(), false);
214+
.generate_downgrade_from_impl(version, next_version, false);
186215

187216
// Add the #[deprecated] attribute when the version is marked as deprecated.
188217
let deprecated_attribute = version
@@ -210,7 +239,8 @@ impl StandaloneContainer {
210239
#container_definition
211240
}
212241

213-
#from_impl
242+
#upgrade_from_impl
243+
#downgrade_from_impl
214244
});
215245
}
216246

@@ -231,13 +261,13 @@ impl StandaloneContainer {
231261
pub(crate) struct ContainerIdents {
232262
/// The ident used in the context of Kubernetes specific code. This ident
233263
/// removes the 'Spec' suffix present in the definition container.
234-
pub(crate) kubernetes: IdentString,
264+
pub kubernetes: IdentString,
235265

236266
/// The original ident, or name, of the versioned container.
237-
pub(crate) original: IdentString,
267+
pub original: IdentString,
238268

239269
/// The ident used in the [`From`] impl.
240-
pub(crate) from: IdentString,
270+
pub from: IdentString,
241271
}
242272

243273
impl ContainerIdents {
@@ -261,32 +291,32 @@ impl ContainerIdents {
261291
}
262292

263293
#[derive(Debug)]
264-
pub(crate) struct ContainerOptions {
265-
pub(crate) kubernetes_options: Option<KubernetesOptions>,
266-
pub(crate) skip_from: bool,
294+
pub struct ContainerOptions {
295+
pub kubernetes_options: Option<KubernetesOptions>,
296+
pub skip_from: bool,
267297
}
268298

269299
#[derive(Debug)]
270-
pub(crate) struct KubernetesOptions {
271-
pub(crate) group: String,
272-
pub(crate) kind: Option<String>,
273-
pub(crate) singular: Option<String>,
274-
pub(crate) plural: Option<String>,
275-
pub(crate) namespaced: bool,
300+
pub struct KubernetesOptions {
301+
pub group: String,
302+
pub kind: Option<String>,
303+
pub singular: Option<String>,
304+
pub plural: Option<String>,
305+
pub namespaced: bool,
276306
// root
277-
pub(crate) crates: KubernetesCrateOptions,
278-
pub(crate) status: Option<Path>,
307+
pub crates: KubernetesCrateOptions,
308+
pub status: Option<Path>,
279309
// derive
280310
// schema
281311
// scale
282312
// printcolumn
283-
pub(crate) shortnames: Vec<String>,
313+
pub shortnames: Vec<String>,
284314
// category
285315
// selectable
286316
// doc
287317
// annotation
288318
// label
289-
pub(crate) skip_merged_crd: bool,
319+
pub skip_merged_crd: bool,
290320
}
291321

292322
impl From<KubernetesArguments> for KubernetesOptions {
@@ -308,12 +338,12 @@ impl From<KubernetesArguments> for KubernetesOptions {
308338
}
309339

310340
#[derive(Debug)]
311-
pub(crate) struct KubernetesCrateOptions {
312-
pub(crate) kube_core: Override<Path>,
313-
pub(crate) k8s_openapi: Override<Path>,
314-
pub(crate) schemars: Override<Path>,
315-
pub(crate) serde: Override<Path>,
316-
pub(crate) serde_json: Override<Path>,
341+
pub struct KubernetesCrateOptions {
342+
pub kube_core: Override<Path>,
343+
pub k8s_openapi: Override<Path>,
344+
pub schemars: Override<Path>,
345+
pub serde: Override<Path>,
346+
pub serde_json: Override<Path>,
317347
}
318348

319349
impl Default for KubernetesCrateOptions {
@@ -396,7 +426,7 @@ impl ToTokens for KubernetesCrateOptions {
396426

397427
/// Wraps a value to indicate whether it is original or has been overridden.
398428
#[derive(Debug)]
399-
pub(crate) enum Override<T> {
429+
pub enum Override<T> {
400430
Default(T),
401431
Overridden(T),
402432
}

0 commit comments

Comments
 (0)