Skip to content

Commit 5ced48b

Browse files
authored
docs(stackable-versioned): Improve docs for module usage (#957)
* docs: Add general module usage doc comments * docs: Add K8s specific module usage docs * docs: Add section about preserve_module flag * docs: Fix Kubernetes doc test
1 parent e5bfee5 commit 5ced48b

File tree

2 files changed

+231
-7
lines changed

2 files changed

+231
-7
lines changed

crates/stackable-versioned-macros/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ quote.workspace = true
4343
[dev-dependencies]
4444
# Only needed for doc tests / examples
4545
stackable-versioned = { path = "../stackable-versioned", features = ["k8s"] }
46-
k8s-openapi = { workspace = true }
46+
k8s-openapi.workspace = true
4747

4848
insta.workspace = true
4949
prettyplease.workspace = true

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

Lines changed: 230 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)