@@ -33,8 +33,8 @@ mod utils;
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
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
3838/// attributes are applied to the generated versioned instances of the
3939/// container.
4040///
@@ -131,6 +131,133 @@ mod utils;
131131/// }
132132/// ```
133133///
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+ ///
134261/// ## Item Actions
135262///
136263/// This crate currently supports three different item actions. Items can
@@ -176,7 +303,7 @@ mod utils;
176303/// ```
177304///
178305/// <details>
179- /// <summary>Generated code </summary>
306+ /// <summary>Expand Generated Code </summary>
180307///
181308/// 1. The field `bar` is not yet present in version `v1alpha1` and is therefore
182309/// not generated.
@@ -235,7 +362,7 @@ mod utils;
235362/// ```
236363///
237364/// <details>
238- /// <summary>Generated code </summary>
365+ /// <summary>Expand Generated Code </summary>
239366///
240367/// 1. Instead of `Default::default()`, the provided function `default_bar()` is
241368/// used. It is of course fully type checked and needs to return the expected
@@ -285,7 +412,7 @@ mod utils;
285412/// ```
286413///
287414/// <details>
288- /// <summary>Generated code </summary>
415+ /// <summary>Expand Generated Code </summary>
289416///
290417/// 1. In version `v1alpha1` the field is named `prev_bar` and uses a `u16`.
291418/// 2. In the next version, `v1beta1`, the field is now named `bar` and uses
@@ -336,7 +463,7 @@ mod utils;
336463/// ```
337464///
338465/// <details>
339- /// <summary>Generated code </summary>
466+ /// <summary>Expand Generated Code </summary>
340467///
341468/// 1. In version `v1alpha1` the field `bar` is not yet deprecated and thus uses
342469/// the name without the `deprecated_` prefix.
@@ -486,6 +613,103 @@ Currently, the following arguments are supported:
486613- `crates`: Override specific crates.
487614- `status`: Sets the specified struct as the status subresource.
488615- `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).
489713"#
490714) ]
491715#[ proc_macro_attribute]
0 commit comments