@@ -33,8 +33,8 @@ mod utils;
33
33
///
34
34
/// It is **important** to note that this macro must be placed before any other
35
35
/// (derive) macros and attributes. Macros supplied before the versioned macro
36
- /// will be erased, because the original struct or enum (container) is erased,
37
- /// and new containers are generated. This ensures that the macros and
36
+ /// will be erased, because the original struct, enum or module (container) is
37
+ /// erased, and new containers are generated. This ensures that the macros and
38
38
/// attributes are applied to the generated versioned instances of the
39
39
/// container.
40
40
///
@@ -131,6 +131,133 @@ mod utils;
131
131
/// }
132
132
/// ```
133
133
///
134
+ /// ## Versioning Items in a Module
135
+ ///
136
+ /// Using the macro on structs and enums is explained in detail in the following
137
+ /// sections. This section is dedicated to explain the usage of the macro when
138
+ /// applied to a module.
139
+ ///
140
+ /// Using the macro on a module has one clear use-case: Versioning multiple
141
+ /// structs and enums at once in **a single file**. Applying the `#[versioned]`
142
+ /// macro to individual containers will result in invalid Rust code which the
143
+ /// compiler rejects. This behaviour can best be explained using the following
144
+ /// example:
145
+ ///
146
+ /// ```ignore
147
+ /// # use stackable_versioned_macros::versioned;
148
+ /// #[versioned(version(name = "v1alpha1"))]
149
+ /// struct Foo {}
150
+ ///
151
+ /// #[versioned(version(name = "v1alpha1"))]
152
+ /// struct Bar {}
153
+ /// ```
154
+ ///
155
+ /// In this example, two different structs are versioned using the same version,
156
+ /// `v1alpha1`. Each macro will now (independently) expand into versioned code.
157
+ /// This will result in the module named `v1alpha1` to be emitted twice, in the
158
+ /// same file. This is invalid Rust code. You cannot define the same module more
159
+ /// than once in the same file.
160
+ ///
161
+ /// <details>
162
+ /// <summary>Expand Generated Invalid Code</summary>
163
+ ///
164
+ /// ```ignore
165
+ /// mod v1alpha1 {
166
+ /// struct Foo {}
167
+ /// }
168
+ ///
169
+ /// mod v1alpha1 {
170
+ /// struct Bar {}
171
+ /// }
172
+ /// ```
173
+ /// </details>
174
+ ///
175
+ /// This behaviour makes it impossible to version multiple containers in the
176
+ /// same file. The only solution would be to put each container into its own
177
+ /// file which in many cases is not needed or even undesired. To solve this
178
+ /// issue, it is thus possible to apply the macro to a module.
179
+ ///
180
+ /// ```
181
+ /// # use stackable_versioned_macros::versioned;
182
+ /// #[versioned(
183
+ /// version(name = "v1alpha1"),
184
+ /// version(name = "v1")
185
+ /// )]
186
+ /// mod versioned {
187
+ /// struct Foo {
188
+ /// bar: usize,
189
+ /// }
190
+ ///
191
+ /// struct Bar {
192
+ /// baz: String,
193
+ /// }
194
+ /// }
195
+ /// ```
196
+ ///
197
+ /// <details>
198
+ /// <summary>Expand Generated Code</summary>
199
+ ///
200
+ /// 1. All containers defined in the module will get versioned. That's why every
201
+ /// version module includes all containers.
202
+ /// 2. Each version will expand to a version module, as expected.
203
+ ///
204
+ /// ```ignore
205
+ /// mod v1alpha1 {
206
+ /// use super::*;
207
+ /// pub struct Foo { // 1
208
+ /// bar: usize,
209
+ /// }
210
+ /// pub struct Bar { // 1
211
+ /// baz: String,
212
+ /// }
213
+ /// }
214
+ ///
215
+ /// mod v1 { // 2
216
+ /// use super::*;
217
+ /// pub struct Foo {
218
+ /// bar: usize,
219
+ /// }
220
+ /// pub struct Bar {
221
+ /// baz: String,
222
+ /// }
223
+ /// }
224
+ /// ```
225
+ /// </details>
226
+ ///
227
+ /// It should be noted that versions are now defined at the module level and
228
+ /// **not** at the struct / enum level. Item actions describes in the following
229
+ /// section can be used as expected.
230
+ ///
231
+ /// ### Preserve Module
232
+ ///
233
+ /// The previous examples completely replaced the `versioned` module with
234
+ /// top-level version modules. This is the default behaviour. Preserving the
235
+ /// module can however be enabled by setting the `preserve_module` flag.
236
+ ///
237
+ /// ```
238
+ /// # use stackable_versioned_macros::versioned;
239
+ /// #[versioned(
240
+ /// version(name = "v1alpha1"),
241
+ /// version(name = "v1"),
242
+ /// preserve_module
243
+ /// )]
244
+ /// mod versioned {
245
+ /// struct Foo {
246
+ /// bar: usize,
247
+ /// }
248
+ ///
249
+ /// struct Bar {
250
+ /// baz: String,
251
+ /// }
252
+ /// }
253
+ /// ```
254
+ ///
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
+ ///
134
261
/// ## Item Actions
135
262
///
136
263
/// This crate currently supports three different item actions. Items can
@@ -176,7 +303,7 @@ mod utils;
176
303
/// ```
177
304
///
178
305
/// <details>
179
- /// <summary>Generated code </summary>
306
+ /// <summary>Expand Generated Code </summary>
180
307
///
181
308
/// 1. The field `bar` is not yet present in version `v1alpha1` and is therefore
182
309
/// not generated.
@@ -235,7 +362,7 @@ mod utils;
235
362
/// ```
236
363
///
237
364
/// <details>
238
- /// <summary>Generated code </summary>
365
+ /// <summary>Expand Generated Code </summary>
239
366
///
240
367
/// 1. Instead of `Default::default()`, the provided function `default_bar()` is
241
368
/// used. It is of course fully type checked and needs to return the expected
@@ -285,7 +412,7 @@ mod utils;
285
412
/// ```
286
413
///
287
414
/// <details>
288
- /// <summary>Generated code </summary>
415
+ /// <summary>Expand Generated Code </summary>
289
416
///
290
417
/// 1. In version `v1alpha1` the field is named `prev_bar` and uses a `u16`.
291
418
/// 2. In the next version, `v1beta1`, the field is now named `bar` and uses
@@ -336,7 +463,7 @@ mod utils;
336
463
/// ```
337
464
///
338
465
/// <details>
339
- /// <summary>Generated code </summary>
466
+ /// <summary>Expand Generated Code </summary>
340
467
///
341
468
/// 1. In version `v1alpha1` the field `bar` is not yet deprecated and thus uses
342
469
/// the name without the `deprecated_` prefix.
@@ -486,6 +613,103 @@ Currently, the following arguments are supported:
486
613
- `crates`: Override specific crates.
487
614
- `status`: Sets the specified struct as the status subresource.
488
615
- `shortname`: Sets the shortname of the CRD.
616
+
617
+ ### Versioning Items in a Module
618
+
619
+ Versioning multiple CRD related structs via a module is supported and common
620
+ rules from [above](#versioning-items-in-a-module) apply here as well. It should
621
+ however be noted, that specifying Kubernetes specific arguments is done on the
622
+ container level instead of on the module level, which is detailed in the
623
+ following example:
624
+
625
+ ```
626
+ # use stackable_versioned_macros::versioned;
627
+ # use kube::CustomResource;
628
+ # use schemars::JsonSchema;
629
+ # use serde::{Deserialize, Serialize};
630
+ #[versioned(
631
+ version(name = "v1alpha1"),
632
+ version(name = "v1")
633
+ )]
634
+ mod versioned {
635
+ #[versioned(k8s(group = "foo.example.org"))]
636
+ #[derive(Clone, Debug, Deserialize, Serialize, CustomResource, JsonSchema)]
637
+ struct FooSpec {
638
+ bar: usize,
639
+ }
640
+
641
+ #[versioned(k8s(group = "bar.example.org"))]
642
+ #[derive(Clone, Debug, Deserialize, Serialize, CustomResource, JsonSchema)]
643
+ struct BarSpec {
644
+ baz: String,
645
+ }
646
+ }
647
+
648
+ # fn main() {
649
+ let merged_crd = Foo::merged_crd(Foo::V1).unwrap();
650
+ println!("{}", serde_yaml::to_string(&merged_crd).unwrap());
651
+ # }
652
+ ```
653
+
654
+ <details>
655
+ <summary>Expand Generated Code</summary>
656
+
657
+ ```ignore
658
+ mod v1alpha1 {
659
+ use super::*;
660
+ #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)]
661
+ #[kube(
662
+ group = "foo.example.org",
663
+ version = "v1alpha1",
664
+ kind = "Foo"
665
+ )]
666
+ pub struct FooSpec {
667
+ pub bar: usize,
668
+ }
669
+
670
+ #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)]
671
+ #[kube(
672
+ group = "bar.example.org",
673
+ version = "v1alpha1",
674
+ kind = "Bar"
675
+ )]
676
+ pub struct BarSpec {
677
+ pub bar: usize,
678
+ }
679
+ }
680
+
681
+ // Automatic From implementations for conversion between versions ...
682
+
683
+ mod v1 {
684
+ use super::*;
685
+ #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)]
686
+ #[kube(
687
+ group = "foo.example.org",
688
+ version = "v1",
689
+ kind = "Foo"
690
+ )]
691
+ pub struct FooSpec {
692
+ pub bar: usize,
693
+ }
694
+
695
+ #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)]
696
+ #[kube(
697
+ group = "bar.example.org",
698
+ version = "v1",
699
+ kind = "Bar"
700
+ )]
701
+ pub struct BarSpec {
702
+ pub bar: usize,
703
+ }
704
+ }
705
+
706
+ // Implementations to create the merged CRDs ...
707
+ ```
708
+ </details>
709
+
710
+ It is possible to include structs and enums which are not CRDs. They are instead
711
+ versioned as expected (without adding the `#[kube]` derive macro and generating
712
+ code to merge CRD versions).
489
713
"#
490
714
) ]
491
715
#[ proc_macro_attribute]
0 commit comments