Skip to content

Commit 81c40d6

Browse files
authored
Merge branch 'main' into feat/stackable-telemetry-options
2 parents ef7319b + 15faafd commit 81c40d6

File tree

7 files changed

+174
-20
lines changed

7 files changed

+174
-20
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#[versioned(version(name = "v1alpha1"), version(name = "v1"), version(name = "v2"))]
2+
// ---
3+
struct Foo {
4+
#[versioned(
5+
// This tests two additional things:
6+
// - that both unquoted and quoted usage works
7+
// - that the renamed name does get picked up correctly by the conversion function
8+
changed(since = "v1", from_type = "u16", from_name = "bar", convert_with = u16_to_u32),
9+
changed(since = "v2", from_type = "u32", convert_with = "u32_to_u64")
10+
)]
11+
baz: u64,
12+
}

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

Lines changed: 42 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/attrs/item/mod.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,19 @@ impl CommonItemAttributes {
237237
);
238238
}
239239
}
240+
241+
// The convert_with argument only makes sense to use when the
242+
// type changed
243+
if let Some(convert_func) = change.convert_with.as_ref() {
244+
if change.from_type.is_none() {
245+
errors.push(
246+
Error::custom(
247+
"the `convert_with` argument must be used in combination with `from_type`",
248+
)
249+
.with_span(&convert_func.span()),
250+
);
251+
}
252+
}
240253
}
241254

242255
errors.finish()
@@ -307,9 +320,10 @@ impl CommonItemAttributes {
307320
actions.insert(
308321
*change.since,
309322
ItemStatus::Change {
323+
convert_with: change.convert_with.as_deref().cloned(),
310324
from_ident: from_ident.clone(),
311-
to_ident: ident,
312325
from_type: from_ty.clone(),
326+
to_ident: ident,
313327
to_type: ty,
314328
},
315329
);
@@ -356,9 +370,10 @@ impl CommonItemAttributes {
356370
actions.insert(
357371
*change.since,
358372
ItemStatus::Change {
373+
convert_with: change.convert_with.as_deref().cloned(),
359374
from_ident: from_ident.clone(),
360-
to_ident: ident,
361375
from_type: from_ty.clone(),
376+
to_ident: ident,
362377
to_type: ty,
363378
},
364379
);
@@ -431,12 +446,14 @@ fn default_default_fn() -> SpannedValue<Path> {
431446
///
432447
/// Example usage:
433448
/// - `changed(since = "...", from_name = "...")`
434-
/// - `changed(since = "...", from_name = "..." from_type="...")`
449+
/// - `changed(since = "...", from_name = "...", from_type="...")`
450+
/// - `changed(since = "...", from_name = "...", from_type="...", convert_with = "...")`
435451
#[derive(Clone, Debug, FromMeta)]
436-
pub(crate) struct ChangedAttributes {
437-
pub(crate) since: SpannedValue<Version>,
438-
pub(crate) from_name: Option<SpannedValue<String>>,
439-
pub(crate) from_type: Option<SpannedValue<Type>>,
452+
pub struct ChangedAttributes {
453+
pub since: SpannedValue<Version>,
454+
pub from_name: Option<SpannedValue<String>>,
455+
pub from_type: Option<SpannedValue<Type>>,
456+
pub convert_with: Option<SpannedValue<Path>>,
440457
}
441458

442459
/// For the deprecated() action

crates/stackable-versioned-macros/src/codegen/item/field.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,18 +167,29 @@ impl VersionedField {
167167
_,
168168
ItemStatus::Change {
169169
from_ident: old_field_ident,
170+
convert_with,
170171
to_ident,
171172
..
172173
},
173-
) => {
174-
quote! {
174+
) => match convert_with {
175+
// The user specified a custom conversion function which
176+
// will be used here instead of the default .into() call
177+
// which utilizes From impls.
178+
Some(convert_fn) => quote! {
179+
#to_ident: #convert_fn(#from_struct_ident.#old_field_ident),
180+
},
181+
// Default .into() call using From impls.
182+
None => quote! {
175183
#to_ident: #from_struct_ident.#old_field_ident.into(),
176-
}
177-
}
184+
},
185+
},
178186
(old, next) => {
179187
let next_field_ident = next.get_ident();
180188
let old_field_ident = old.get_ident();
181189

190+
// NOTE (@Techassi): Do we really need .into() here. I'm
191+
// currently not sure why it is there and if it is needed
192+
// in some edge cases.
182193
quote! {
183194
#next_field_ident: #from_struct_ident.#old_field_ident.into(),
184195
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl From<&ModuleAttributes> for Vec<VersionDefinition> {
7272
}
7373

7474
#[derive(Debug, PartialEq)]
75-
pub(crate) enum ItemStatus {
75+
pub enum ItemStatus {
7676
Addition {
7777
ident: IdentString,
7878
default_fn: Path,
@@ -81,6 +81,7 @@ pub(crate) enum ItemStatus {
8181
ty: Type,
8282
},
8383
Change {
84+
convert_with: Option<Path>,
8485
from_ident: IdentString,
8586
to_ident: IdentString,
8687
from_type: Type,

crates/stackable-versioned-macros/src/lib.rs

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ mod utils;
2929
/// example, `#[automatically_derived]` and `#[allow(deprecated)]` are removed
3030
/// in most examples to reduce visual clutter.
3131
///
32-
/// ## Declaring Versions
32+
/// <div class="warning">
3333
///
3434
/// It is **important** to note that this macro must be placed before any other
3535
/// (derive) macros and attributes. Macros supplied before the versioned macro
@@ -38,16 +38,29 @@ mod utils;
3838
/// attributes are applied to the generated versioned instances of the
3939
/// container.
4040
///
41+
/// </div>
42+
///
43+
/// ## Declaring Versions
44+
///
4145
/// Before any of the fields or variants can be versioned, versions need to be
4246
/// declared at the container level. Each version currently supports two
4347
/// parameters: `name` and the `deprecated` flag. The `name` must be a valid
4448
/// (and supported) format.
4549
///
4650
/// <div class="warning">
51+
///
4752
/// Currently, only Kubernetes API versions are supported. The macro checks each
4853
/// declared version and reports any error encountered during parsing.
54+
///
4955
/// </div>
5056
///
57+
/// It should be noted that the defined struct always represents the **latest**
58+
/// version, eg: when defining three versions `v1alpha1`, `v1beta1`, and `v1`,
59+
/// the struct will describe the structure of the data in `v1`. This behaviour
60+
/// is especially noticeable in the [`changed()`](#changed-action) action which
61+
/// works "backwards" by describing how a field looked before the current
62+
/// (latest) version.
63+
///
5164
/// ```
5265
/// # use stackable_versioned_macros::versioned;
5366
/// #[versioned(version(name = "v1alpha1"))]
@@ -252,12 +265,6 @@ mod utils;
252265
/// }
253266
/// ```
254267
///
255-
/// <div class="warning">
256-
/// It is planned to move the <code>preserve_module</code> flag into the
257-
/// <code>options()</code> argument list, but currently seems tricky to
258-
/// implement.
259-
/// </div>
260-
///
261268
/// ## Item Actions
262269
///
263270
/// This crate currently supports three different item actions. Items can
@@ -393,6 +400,10 @@ mod utils;
393400
/// - `since` to indicate since which version the item is changed.
394401
/// - `from_name` to indicate from which previous name the field is renamed.
395402
/// - `from_type` to indicate from which previous type the field is changed.
403+
/// - `convert_with` to provide a custom conversion function instead of using
404+
/// a [`From`] implementation by default. This argument can only be used in
405+
/// combination with the `from_type` argument. The expected function signature
406+
/// is: `fn (OLD_TYPE) -> NEW_TYPE`. This function must not fail.
396407
///
397408
/// ```
398409
/// # use stackable_versioned_macros::versioned;
@@ -514,6 +525,61 @@ mod utils;
514525
/// This automatic generation can be skipped to enable a custom implementation
515526
/// for more complex conversions.
516527
///
528+
/// ### Custom conversion function at field level
529+
///
530+
/// As stated in the [`changed()`](#changed-action) section, a custom conversion
531+
/// function can be provided using the `convert_with` argument. A simple example
532+
/// looks like this:
533+
///
534+
/// ```
535+
/// # use stackable_versioned_macros::versioned;
536+
/// #[versioned(
537+
/// version(name = "v1alpha1"),
538+
/// version(name = "v1beta1")
539+
/// )]
540+
/// pub struct Foo {
541+
/// #[versioned(changed(
542+
/// since = "v1beta1",
543+
/// from_type = "u8",
544+
/// convert_with = "u8_to_u16"
545+
/// ))]
546+
/// bar: u16,
547+
/// }
548+
///
549+
/// fn u8_to_u16(old: u8) -> u16 {
550+
/// old as u16
551+
/// }
552+
/// ```
553+
///
554+
/// <details>
555+
/// <summary>Expand Generated Code</summary>
556+
///
557+
/// ```ignore
558+
/// pub mod v1alpha1 {
559+
/// use super::*;
560+
/// pub struct Foo {
561+
/// pub bar: u8,
562+
/// }
563+
/// }
564+
///
565+
/// impl ::std::convert::From<v1alpha1::Foo> for v1beta1::Foo {
566+
/// fn from(__sv_foo: v1alpha1::Foo) -> Self {
567+
/// Self {
568+
/// bar: u8_to_u16(__sv_foo.bar),
569+
/// }
570+
/// }
571+
/// }
572+
///
573+
/// pub mod v1beta1 {
574+
/// use super::*;
575+
/// pub struct Foo {
576+
/// pub bar: u16,
577+
/// }
578+
/// }
579+
/// ```
580+
///
581+
/// </details>
582+
///
517583
/// ### Skipping at the Container Level
518584
///
519585
/// Disabling this behavior at the container level results in no `From`
@@ -601,6 +667,11 @@ println!("{}", serde_yaml::to_string(&merged_crd).unwrap());
601667
# }
602668
```
603669
670+
The generated `merged_crd` method is a wrapper around [kube's `merge_crds`][1]
671+
function. It automatically calls the `crd` methods of the CRD in all of its
672+
versions and additionally provides a strongly typed selector for the stored
673+
API version.
674+
604675
Currently, the following arguments are supported:
605676
606677
- `group`: Set the group of the CR object, usually the domain of the company.

crates/stackable-versioned/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct Foo {
3030
/// My docs
3131
#[versioned(
3232
added(since = "v1alpha1"),
33-
renamed(since = "v1beta1", from = "gau"),
33+
changed(since = "v1beta1", from_name = "gau"),
3434
deprecated(since = "v2", note = "not required anymore")
3535
)]
3636
deprecated_bar: usize,

0 commit comments

Comments
 (0)