From 0111a5c730298cb2711c8f72cf3ac8645928149a Mon Sep 17 00:00:00 2001 From: Techassi Date: Wed, 11 Sep 2024 16:52:52 +0200 Subject: [PATCH 1/7] docs(stackable-versioned): Update usage guide --- crates/stackable-versioned-macros/src/lib.rs | 248 ++++++++++++++----- crates/stackable-versioned/src/lib.rs | 24 -- 2 files changed, 183 insertions(+), 89 deletions(-) diff --git a/crates/stackable-versioned-macros/src/lib.rs b/crates/stackable-versioned-macros/src/lib.rs index d01ffb2ef..0a6465f92 100644 --- a/crates/stackable-versioned-macros/src/lib.rs +++ b/crates/stackable-versioned-macros/src/lib.rs @@ -8,50 +8,95 @@ mod attrs; mod codegen; mod consts; -/// This macro enables generating versioned structs. +/// This macro enables generating versioned structs and enums. /// -/// ## Usage Guide +/// # Usage Guide /// -/// ### Quickstart +/// In this guide, code blocks usually come in pairs. The first code block +/// describes how the macro is used. The second expandable block displays the +/// generated piece of code for explanation purposes. It should be noted, that +/// the exact code can diverge from what is being depicted in this guide. For +/// example, `#[automatically_derived]` and `#[allow(deprecated)]` are removed +/// in most examples to reduce visual clutter. +/// +/// ## Declaring Versions +/// +/// It is **important** to note that this macro must be placed before any other +/// (derive) macros and attributes. This ensures that the macros and attributes +/// are applied to the generated versioned instances of the struct or enum. +/// +/// Before any of the fields or variants can be versioned, versions need to be +/// declared at the container level. Each version currently supports two +/// parameters: `name` and the `deprecated` flag. The `name` must be a valid +/// (and supported) format - currently, only Kubernetes API versions are +/// supported. The macro checks each declared version and reports any error +/// encountered during parsing. /// /// ``` /// # use stackable_versioned_macros::versioned; -/// #[versioned( -/// version(name = "v1alpha1"), -/// version(name = "v1beta1"), -/// version(name = "v1"), -/// version(name = "v2"), -/// version(name = "v3") -/// )] +/// #[versioned(version(name = "v1alpha1"))] /// struct Foo { -/// /// My docs -/// #[versioned( -/// added(since = "v1beta1"), -/// changed(since = "v1", from_name = "gau"), -/// deprecated(since = "v2", note = "not empty") -/// )] -/// deprecated_bar: usize, -/// baz: bool, +/// bar: usize, /// } /// ``` /// -/// ### Declaring Versions +///
+/// Generated code +/// +/// 1. The `#[automatically_derived]` attribute indicates that the following +/// piece of code is automatically generated by a macro instead of being +/// handwritten by a developer. This information is used by cargo and rustc. +/// 2. For each declared version, a new module containing the struct or enum +/// (container) is generated. This enables you to reference the container by +/// versions via `v1alpha1::Foo`. +/// 3. This `use` statement gives the generated containers access to the imports +/// at the top of the file. This is a convenience, because otherwise you +/// would need to prefix used items with `super::`. Additionally, other +/// macros can have trouble using items referred to with `super::`. +/// +/// ```ignore +/// #[automatically_derived] // 1 +/// mod v1alpha1 { // 2 +/// use super::*; // 3 +/// pub struct Foo { +/// bar: usize, +/// } +/// } +/// ``` +///
+/// +/// ### Deprecation of a Version /// -/// Before any of the fields can be versioned, versions need to be declared at -/// the container level. Each version currently supports two parameters: `name` -/// and the `deprecated` flag. The `name` must be a valid (and supported) -/// format. The macro checks each declared version and reports any error -/// encountered during parsing. /// The `deprecated` flag marks the version as deprecated. This currently adds /// the `#[deprecated]` attribute to the appropriate piece of code. /// /// ``` /// # use stackable_versioned_macros::versioned; -/// #[versioned( -/// version(name = "v1alpha1", deprecated) -/// )] -/// struct Foo {} +/// #[versioned(version(name = "v1alpha1", deprecated))] +/// struct Foo { +/// bar: usize, +/// } +/// ``` +/// +///
+/// Generated code +/// +/// 1. The `deprecated` flag will generate a `#[deprecated]` attribute and the +/// note is automatically generated. +/// +/// ```ignore +/// #[automatically_derived] +/// #[deprecated = "Version v1alpha1 is deprecated"] // 1 +/// mod v1alpha1 { +/// use super::*; +/// pub struct Foo { +/// pub bar: usize, +/// } +/// } /// ``` +///
+/// +/// ### Version Sorting /// /// Additionally, it is ensured that each version is unique. Declaring the same /// version multiple times will result in an error. Furthermore, declaring the @@ -65,37 +110,94 @@ mod consts; /// version(name = "v1alpha1"), /// options(allow_unsorted) /// )] -/// struct Foo {} +/// struct Foo { +/// bar: usize, +/// } /// ``` /// -/// ### Field Actions +/// ## Item Actions /// -/// This library currently supports three different field actions. Fields can +/// This library currently supports three different item actions. Items can /// be added, renamed and deprecated. The macro ensures that these actions /// adhere to the following set of rules: /// -/// - Fields cannot be added and deprecated in the same version. -/// - Fields cannot be added and renamed in the same version. -/// - Fields cannot be renamed and deprecated in the same version. -/// - Fields added in version _a_, renamed _0...n_ times in versions -/// b1, b2, ..., bn and deprecated in -/// version _c_ must ensure _a < b1, b2, ..., -/// bn < c_. -/// - All field actions must use previously declared versions. Using versions -/// not present at the container level will result in an error. +/// 1. Items cannot be added and deprecated in the same version. +/// 2. Items cannot be added and changed in the same version. +/// 3. Items cannot be changed and deprecated in the same version. +/// 4. Items added in version _a_, renamed _0...n_ times in versions +/// b1, ..., bn and deprecated in +/// version _c_ must ensure _a < b1, ..., bn < c_. +/// 5. All item actions must use previously declared versions. Using versions +/// not present at the container level will result in an error. /// -/// For fields marked as deprecated, two additional rules apply: +/// For items marked as deprecated, one additional rule applies: /// -/// - Fields must start with the `deprecated_` prefix. -/// - The deprecation note cannot be empty. +/// - Fields must start with the `deprecated_` and variants with the +/// `Deprecated` prefix. This is enforced because Kubernetes doesn't allow +/// removing fields in CRDs entirely. Instead, they should be marked as +/// deprecated. By convention this is done with the `deprecated` prefix. /// -/// ### Auto-generated [`From`] Implementations +/// ### Added Action +/// +/// This action indicates that a item is added in a particular version. +/// Available parameters are: /// -/// To enable smooth version upgrades of the same struct, the macro automatically -/// generates [`From`] implementations. On a high level, code generated for two -/// versions _a_ and _b_, with _a < b_ looks like this: `impl From for b`. +/// - `since` to indicate since which version the item is present. +/// - `default_fn` to customize the default function used to populate the item +/// in auto-generated [`From`] implementations. +/// +/// ``` +/// # use stackable_versioned_macros::versioned; +/// #[versioned(version(name = "v1alpha1"), version(name = "v1beta1"))] +/// pub struct Foo { +/// #[versioned(added(since = "v1beta1"))] +/// bar: usize, +/// baz: bool, +/// } +/// ``` +/// +///
+/// Generated code +/// +/// 1. The field `bar` is not yet present in version `v1alpha1` and is therefore +/// not generated. +/// 2. Now the field `bar` is present in version `v1beta1`. /// /// ```ignore +/// pub mod v1alpha1 { +/// use super::*; +/// pub struct Foo { // 1 +/// pub baz: bool, +/// } +/// } +/// +/// pub mod v1beta1 { +/// use super::*; +/// pub struct Foo { +/// pub bar: usize, // 2 +/// pub baz: bool, +/// } +/// } +/// ``` +///
+/// +/// ### Changed Action +/// +/// TODO. +/// +/// ### Deprecated Action +/// +/// TODO. +/// +/// ### Auto-generated [`From`] Implementations +/// +/// To enable smooth version upgrades of the same container, the macro +/// automatically generates [`From`] implementations. On a high level, code +/// generated for two versions _a_ and _b_, with _a < b_ looks like this: +/// `impl From
for b`. +/// +/// ``` +/// # use stackable_versioned_macros::versioned; /// #[versioned( /// version(name = "v1alpha1"), /// version(name = "v1beta1"), @@ -104,62 +206,78 @@ mod consts; /// pub struct Foo { /// #[versioned( /// added(since = "v1beta1"), -/// deprecated(since = "v1", note = "not needed") +/// deprecated(since = "v1") /// )] /// deprecated_bar: usize, /// baz: bool, /// } +/// ``` /// -/// // Produces ... +///
+/// Generated code /// -/// #[automatically_derived] +/// 1. The field `bar` is not yet present in version `v1alpha1` and is therefore +/// not generated. +/// 2. Now the field `bar` is present and uses `Default::default()` to populate +/// the field during conversion. This function can be customized as shown +/// later in this guide. +/// 3. In version `v1` the field `bar` is deprecated and as such includes the +/// `deprecated_` prefix. Additionally, the `#[deprecated]` attribute is +/// added to indicate that this part of the Rust code is deprecated. The +/// note is optional. +/// +/// ```ignore /// pub mod v1alpha1 { -/// pub struct Foo { +/// use super::*; +/// pub struct Foo { // 1 /// pub baz: bool, /// } /// } -/// #[automatically_derived] -/// #[allow(deprecated)] +/// /// impl From for v1beta1::Foo { /// fn from(__sv_foo: v1alpha1::Foo) -> Self { /// Self { -/// bar: std::default::Default::default(), +/// bar: ::std::default::Default::default(), // 2 /// baz: __sv_foo.baz, /// } /// } /// } -/// #[automatically_derived] +/// /// pub mod v1beta1 { +/// use super::*; /// pub struct Foo { /// pub bar: usize, /// pub baz: bool, /// } /// } -/// #[automatically_derived] -/// #[allow(deprecated)] +/// /// impl From for v1::Foo { /// fn from(__sv_foo: v1beta1::Foo) -> Self { /// Self { -/// deprecated_bar: __sv_foo.bar, +/// deprecated_bar: __sv_foo.bar, // 3 /// baz: __sv_foo.baz, /// } /// } /// } -/// #[automatically_derived] +/// /// pub mod v1 { +/// use super::*; /// pub struct Foo { -/// #[deprecated = "not needed"] +/// #[deprecated] // 3 /// pub deprecated_bar: usize, /// pub baz: bool, /// } /// } /// ``` +///
+/// +/// ### Skip [`From`] Generation /// -/// #### Skip [`From`] generation +/// Generation of auto-generated [`From`] implementations can be skipped at the +/// container and version level. This enables customization of the +/// implementations if the default implementation is not sufficient. /// -/// Generation of these [`From`] implementations can be skipped at the container -/// and version level. This enables customization of the implementations if the -/// default implementation is not sufficient. +/// #### Skipping at the Container Level /// /// ``` /// # use stackable_versioned_macros::versioned; @@ -179,7 +297,7 @@ mod consts; /// } /// ``` /// -/// #### Customize Default Function for Added Fields +/// ### Customize Default Function for Added Fields /// /// It is possible to customize the default function used in the generated /// [`From`] implementation for populating added fields. By default, diff --git a/crates/stackable-versioned/src/lib.rs b/crates/stackable-versioned/src/lib.rs index 4f3de2f34..3fa980715 100644 --- a/crates/stackable-versioned/src/lib.rs +++ b/crates/stackable-versioned/src/lib.rs @@ -3,30 +3,6 @@ //! data type. This will be extended to support SemVer versions, as well as //! custom version formats in the future. //! -//! ## Usage Guide -//! -//! ``` -//! use stackable_versioned::versioned; -//! -//! #[versioned( -//! version(name = "v1alpha1"), -//! version(name = "v1beta1"), -//! version(name = "v1"), -//! version(name = "v2"), -//! version(name = "v3") -//! )] -//! struct Foo { -//! /// My docs -//! #[versioned( -//! added(since = "v1beta1"), -//! changed(since = "v1", from_name = "gau"), -//! deprecated(since = "v2", note = "not empty") -//! )] -//! deprecated_bar: usize, -//! baz: bool, -//! } -//! ``` -//! //! See [`versioned`] for an in-depth usage guide and a list of supported //! parameters. From 76c686be41b7367c14d726c2023696a8126f2f9a Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 12 Sep 2024 13:01:22 +0200 Subject: [PATCH 2/7] docs: Add action specific docs, add K8s docs --- crates/stackable-versioned-macros/Cargo.toml | 4 + crates/stackable-versioned-macros/src/lib.rs | 251 ++++++++++++++----- crates/stackable-versioned/Cargo.toml | 9 + 3 files changed, 204 insertions(+), 60 deletions(-) diff --git a/crates/stackable-versioned-macros/Cargo.toml b/crates/stackable-versioned-macros/Cargo.toml index af29f3d06..86b231592 100644 --- a/crates/stackable-versioned-macros/Cargo.toml +++ b/crates/stackable-versioned-macros/Cargo.toml @@ -6,6 +6,10 @@ license.workspace = true edition.workspace = true repository.workspace = true +# Enable all features to ensure content appears in the online documentation. +[package.metadata."docs.rs"] +all-features = true + # cargo-udeps throws an error that these dependencies are unused. They are, # however, used in K8s specific test cases. This is a false-positive and an # apparent limitation of cargo-udeps. These entries can be removed once diff --git a/crates/stackable-versioned-macros/src/lib.rs b/crates/stackable-versioned-macros/src/lib.rs index 0a6465f92..1d3174448 100644 --- a/crates/stackable-versioned-macros/src/lib.rs +++ b/crates/stackable-versioned-macros/src/lib.rs @@ -139,16 +139,19 @@ mod consts; /// /// ### Added Action /// -/// This action indicates that a item is added in a particular version. +/// This action indicates that an item is added in a particular version. /// Available parameters are: /// /// - `since` to indicate since which version the item is present. -/// - `default_fn` to customize the default function used to populate the item +/// - `default` to customize the default function used to populate the item /// in auto-generated [`From`] implementations. /// /// ``` /// # use stackable_versioned_macros::versioned; -/// #[versioned(version(name = "v1alpha1"), version(name = "v1beta1"))] +/// #[versioned( +/// version(name = "v1alpha1"), +/// version(name = "v1beta1") +/// )] /// pub struct Foo { /// #[versioned(added(since = "v1beta1"))] /// bar: usize, @@ -161,54 +164,106 @@ mod consts; /// /// 1. The field `bar` is not yet present in version `v1alpha1` and is therefore /// not generated. -/// 2. Now the field `bar` is present in version `v1beta1`. +/// 2. Now the field `bar` is present and uses `Default::default()` to populate +/// the field during conversion. This function can be customized as shown +/// later in this guide. /// /// ```ignore /// pub mod v1alpha1 { /// use super::*; -/// pub struct Foo { // 1 +/// pub struct Foo { // 1 /// pub baz: bool, /// } /// } /// +/// impl From for v1beta1::Foo { +/// fn from(foo: v1alpha1::Foo) -> Self { +/// Self { +/// bar: Default::default(), // 2 +/// baz: foo.baz, +/// } +/// } +/// } +/// /// pub mod v1beta1 { /// use super::*; /// pub struct Foo { -/// pub bar: usize, // 2 +/// pub bar: usize, // 2 /// pub baz: bool, /// } /// } /// ``` /// /// -/// ### Changed Action +/// #### Custom Default Function /// -/// TODO. +/// To customize the default function used in the generated `From` implementation +/// you can use the `default` parameter. It expects a path to a function without +/// braces. /// -/// ### Deprecated Action +/// ``` +/// # use stackable_versioned_macros::versioned; +/// #[versioned( +/// version(name = "v1alpha1"), +/// version(name = "v1beta1") +/// )] +/// pub struct Foo { +/// #[versioned(added(since = "v1beta1", default = "default_bar"))] +/// bar: usize, +/// baz: bool, +/// } +/// +/// fn default_bar() -> usize { +/// 42 +/// } +/// ``` /// -/// TODO. +///
+/// Generated code /// -/// ### Auto-generated [`From`] Implementations +/// 1. Instead of `Default::default()`, the provided function `default_bar()` is +/// used. It is of course fully type checked and needs to return the expected +/// type (`usize` in this case). /// -/// To enable smooth version upgrades of the same container, the macro -/// automatically generates [`From`] implementations. On a high level, code -/// generated for two versions _a_ and _b_, with _a < b_ looks like this: -/// `impl From for b`. +/// ```ignore +/// // Snip +/// +/// impl From for v1beta1::Foo { +/// fn from(foo: v1alpha1::Foo) -> Self { +/// Self { +/// bar: default_bar(), // 1 +/// baz: foo.baz, +/// } +/// } +/// } +/// +/// // Snip +/// ``` +///
+/// +/// ### Changed Action +/// +/// This action indicates that an item is changed in a particular version. It +/// combines renames and type changes into a single action. You can choose to +/// change the name, change the type or do both. Available parameters are: +/// +/// - `since` to indicate since which version the item is changed. +/// - `from_name` to indicate from which previous name the field is renamed. +/// - `from_type` to indicate from which previous type the field is changed. /// /// ``` /// # use stackable_versioned_macros::versioned; /// #[versioned( /// version(name = "v1alpha1"), -/// version(name = "v1beta1"), -/// version(name = "v1") +/// version(name = "v1beta1") /// )] /// pub struct Foo { -/// #[versioned( -/// added(since = "v1beta1"), -/// deprecated(since = "v1") -/// )] -/// deprecated_bar: usize, +/// #[versioned(changed( +/// since = "v1beta1", +/// from_name = "prev_bar", +/// from_type = "u16" +/// ))] +/// bar: usize, /// baz: bool, /// } /// ``` @@ -216,29 +271,25 @@ mod consts; ///
/// Generated code /// -/// 1. The field `bar` is not yet present in version `v1alpha1` and is therefore -/// not generated. -/// 2. Now the field `bar` is present and uses `Default::default()` to populate -/// the field during conversion. This function can be customized as shown -/// later in this guide. -/// 3. In version `v1` the field `bar` is deprecated and as such includes the -/// `deprecated_` prefix. Additionally, the `#[deprecated]` attribute is -/// added to indicate that this part of the Rust code is deprecated. The -/// note is optional. +/// 1. In version `v1alpha1` the field is named `prev_bar` and uses a `u16`. +/// 2. In the next version, `v1beta1`, the field is now named `bar` and uses +/// `usize` instead of a `u16`. The `From` implementation transforms the +/// type automatically via the `.into()` call. /// /// ```ignore /// pub mod v1alpha1 { /// use super::*; -/// pub struct Foo { // 1 +/// pub struct Foo { +/// pub prev_bar: u16, // 1 /// pub baz: bool, /// } /// } /// /// impl From for v1beta1::Foo { -/// fn from(__sv_foo: v1alpha1::Foo) -> Self { +/// fn from(foo: v1alpha1::Foo) -> Self { /// Self { -/// bar: ::std::default::Default::default(), // 2 -/// baz: __sv_foo.baz, +/// bar: foo.prev_bar.into(), // 2 +/// baz: foo.baz, /// } /// } /// } @@ -246,24 +297,62 @@ mod consts; /// pub mod v1beta1 { /// use super::*; /// pub struct Foo { -/// pub bar: usize, +/// pub bar: usize, // 2 /// pub baz: bool, /// } /// } +/// ``` +///
/// -/// impl From for v1::Foo { -/// fn from(__sv_foo: v1beta1::Foo) -> Self { +/// ### Deprecated Action +/// +/// This action indicates that an item is deprecated in a particular version. +/// Deprecated items are not removed. +/// +/// ``` +/// # use stackable_versioned_macros::versioned; +/// #[versioned(version(name = "v1alpha1"), version(name = "v1beta1"))] +/// pub struct Foo { +/// #[versioned(deprecated(since = "v1beta1"))] +/// deprecated_bar: usize, +/// baz: bool, +/// } +/// ``` +/// +///
+/// Generated code +/// +/// 1. In version `v1alpha1` the field `bar` is not yet deprecated and thus uses +/// the name without the `deprecated_` prefix. +/// 2. In version `v1beta1` the field is deprecated and now includes the +/// `deprecated_` prefix. It also uses the `#[deprecated]` attribute to +/// indicate to Clippy this part of Rust code is deprecated. Therefore, the +/// `From` implementation includes `#[allow(deprecated)]` to allow the +/// usage of deprecated items in automatically generated code. +/// +/// ```ignore +/// pub mod v1alpha1 { +/// use super::*; +/// pub struct Foo { +/// pub bar: usize, // 1 +/// pub baz: bool, +/// } +/// } +/// +/// #[allow(deprecated)] // 2 +/// impl From for v1beta1::Foo { +/// fn from(foo: v1alpha1::Foo) -> Self { /// Self { -/// deprecated_bar: __sv_foo.bar, // 3 -/// baz: __sv_foo.baz, +/// deprecated_bar: foo.bar, // 2 +/// baz: foo.baz, /// } /// } /// } /// -/// pub mod v1 { +/// pub mod v1beta1 { /// use super::*; /// pub struct Foo { -/// #[deprecated] // 3 +/// #[deprecated] // 2 /// pub deprecated_bar: usize, /// pub baz: bool, /// } @@ -271,13 +360,22 @@ mod consts; /// ``` ///
/// -/// ### Skip [`From`] Generation +/// ## Auto-generated `From` Implementations +/// +/// To enable smooth version upgrades of the same container, the macro +/// automatically generates `From` implementations. On a high level, code +/// generated for two versions _a_ and _b_, with _a < b_ looks like this: +/// `impl From
for b`. As you can see, only upgrading is currently supported. +/// Downgrading, so going from a higher version to a lower one, is not supported +/// at the moment. +/// +/// This automatic generation can be skipped to for example enable a custom +/// implementation for more complex conversions. /// -/// Generation of auto-generated [`From`] implementations can be skipped at the -/// container and version level. This enables customization of the -/// implementations if the default implementation is not sufficient. +/// ### Skipping at the Container Level /// -/// #### Skipping at the Container Level +/// Disabling this feature on the container level results in no `From` +/// implementation for all versions. /// /// ``` /// # use stackable_versioned_macros::versioned; @@ -290,39 +388,72 @@ mod consts; /// pub struct Foo { /// #[versioned( /// added(since = "v1beta1"), -/// deprecated(since = "v1", note = "not needed") +/// deprecated(since = "v1") /// )] /// deprecated_bar: usize, /// baz: bool, /// } /// ``` /// -/// ### Customize Default Function for Added Fields +/// ### Skipping at the Version Level /// -/// It is possible to customize the default function used in the generated -/// [`From`] implementation for populating added fields. By default, -/// [`Default::default()`] is used. +/// Disabling this feature at the version level results in no `From` +/// implementation for that particular version. This can be read as "skip +/// generation for converting _this_ version to the next one". In the example +/// below no conversion between version `v1beta1` and `v1` is generated. /// /// ``` /// # use stackable_versioned_macros::versioned; /// #[versioned( /// version(name = "v1alpha1"), -/// version(name = "v1beta1"), +/// version(name = "v1beta1", skip(from)), /// version(name = "v1") /// )] /// pub struct Foo { /// #[versioned( -/// added(since = "v1beta1", default = "default_bar"), -/// deprecated(since = "v1", note = "not needed") +/// added(since = "v1beta1"), +/// deprecated(since = "v1") /// )] /// deprecated_bar: usize, /// baz: bool, /// } -/// -/// fn default_bar() -> usize { -/// 42 -/// } /// ``` +/// +/// ## Kubernetes-specific Features +/// +/// This macro also offers support for Kubernetes-specific versioning, +/// especially for CustomResourceDefinitions, short CRDs. These features are +/// completely opt-in. You need to enable the `k8s` feature (which enables +/// optional dependencies) and use the `k8s()` parameter in the macro. +/// +#[cfg_attr( + feature = "k8s", + doc = r#" +``` +# use stackable_versioned_macros::versioned; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[versioned( + version(name = "v1alpha1"), + version(name = "v1beta1"), + version(name = "v1"), + k8s(group = "example.com") +)] +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] +pub struct FooSpec { + #[versioned( + added(since = "v1beta1"), + changed(since = "v1", from_name = "prev_bar", from_type = "u16") + )] + bar: usize, + baz: bool, +} +let merged_crd = Foo::merged_crd("v1").unwrap(); +println!("{}", serde_yaml::to_string(&merged_crd).unwrap()); +``` +"# +)] #[proc_macro_attribute] pub fn versioned(attrs: TokenStream, input: TokenStream) -> TokenStream { let attrs = match NestedMeta::parse_meta_list(attrs.into()) { @@ -334,7 +465,7 @@ pub fn versioned(attrs: TokenStream, input: TokenStream) -> TokenStream { }; // NOTE (@Techassi): For now, we can just use the DeriveInput type here, - // because we only support structs (and eventually enums) to be versioned. + // because we only support structs end enums to be versioned. // In the future - if we decide to support modules - this requires // adjustments to also support modules. One possible solution might be to // use an enum with two variants: Container(DeriveInput) and diff --git a/crates/stackable-versioned/Cargo.toml b/crates/stackable-versioned/Cargo.toml index 5bbb92f50..fb47cff17 100644 --- a/crates/stackable-versioned/Cargo.toml +++ b/crates/stackable-versioned/Cargo.toml @@ -6,5 +6,14 @@ license.workspace = true edition.workspace = true repository.workspace = true +# Enable all features to ensure content appears in the online documentation. +[package.metadata."docs.rs"] +all-features = true + +[features] +full = ["k8s"] +# Forward the k8s feature to the underlying macro crate +k8s = ["stackable-versioned-macros/k8s"] + [dependencies] stackable-versioned-macros = { path = "../stackable-versioned-macros" } From 0ddd4c088df2fbcb3a3c221c96e34f329cae74be Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 12 Sep 2024 13:01:43 +0200 Subject: [PATCH 3/7] chore: Add validation TODOs --- crates/stackable-versioned-macros/src/attrs/common/item.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/stackable-versioned-macros/src/attrs/common/item.rs b/crates/stackable-versioned-macros/src/attrs/common/item.rs index c6ee146eb..3fa8c5695 100644 --- a/crates/stackable-versioned-macros/src/attrs/common/item.rs +++ b/crates/stackable-versioned-macros/src/attrs/common/item.rs @@ -296,6 +296,7 @@ impl ItemAttributes { } } +// TODO (@Techassi): Add validation for when default_fn is "" (empty path). /// For the added() action /// /// Example usage: @@ -317,6 +318,10 @@ fn default_default_fn() -> SpannedValue { ) } +// TODO (@Techassi): Add validation for when from_name AND from_type or both +// none => is this action needed in the first place? +// TODO (@Techassi): Add validation that the from_name mustn't include the +// deprecated prefix. /// For the changed() action /// /// Example usage: From d44336e886fe22afa251d21dd7a54c9e5fb1a849 Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 12 Sep 2024 14:25:20 +0200 Subject: [PATCH 4/7] docs: Update top-level crate docs --- crates/stackable-versioned/src/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/stackable-versioned/src/lib.rs b/crates/stackable-versioned/src/lib.rs index 3fa980715..5080f34af 100644 --- a/crates/stackable-versioned/src/lib.rs +++ b/crates/stackable-versioned/src/lib.rs @@ -1,7 +1,13 @@ -//! This crate enables versioning of structs (and enums in the future). It -//! currently supports Kubernetes API versions while declaring versions on a -//! data type. This will be extended to support SemVer versions, as well as -//! custom version formats in the future. +//! This crate enables versioning of structs and enums through procedural +//! macros. +//! +//! Currently supported versioning schemes: +//! +//! - Kubernetes API versions (eg: `v1alpha1`, `v1beta1`, `v1`, `v2`), with +//! optional support for generating CRDs. +//! +//! Support will be extended to SemVer versions, as well as custom version +//! formats in the future. //! //! See [`versioned`] for an in-depth usage guide and a list of supported //! parameters. From 9a0b6347714795db334d8540f9d164e8abfbfde2 Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 12 Sep 2024 14:53:41 +0200 Subject: [PATCH 5/7] docs: Hide unused trait --- crates/stackable-versioned/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/stackable-versioned/src/lib.rs b/crates/stackable-versioned/src/lib.rs index 5080f34af..1f2d99d13 100644 --- a/crates/stackable-versioned/src/lib.rs +++ b/crates/stackable-versioned/src/lib.rs @@ -14,6 +14,8 @@ pub use stackable_versioned_macros::*; +// Unused for now, might get picked up again in the future. +#[doc(hidden)] pub trait AsVersionStr { const VERSION: &'static str; From c320e8e852adc7c6d365b121c7e8cb913cf390aa Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 12 Sep 2024 16:10:11 +0200 Subject: [PATCH 6/7] chore: Apply suggestions Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- .../src/attrs/common/item.rs | 2 +- crates/stackable-versioned-macros/src/lib.rs | 34 +++++++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/crates/stackable-versioned-macros/src/attrs/common/item.rs b/crates/stackable-versioned-macros/src/attrs/common/item.rs index 3fa8c5695..839ae3ec6 100644 --- a/crates/stackable-versioned-macros/src/attrs/common/item.rs +++ b/crates/stackable-versioned-macros/src/attrs/common/item.rs @@ -318,7 +318,7 @@ fn default_default_fn() -> SpannedValue { ) } -// TODO (@Techassi): Add validation for when from_name AND from_type or both +// TODO (@Techassi): Add validation for when from_name AND from_type are both // none => is this action needed in the first place? // TODO (@Techassi): Add validation that the from_name mustn't include the // deprecated prefix. diff --git a/crates/stackable-versioned-macros/src/lib.rs b/crates/stackable-versioned-macros/src/lib.rs index 1d3174448..70e40dc67 100644 --- a/crates/stackable-versioned-macros/src/lib.rs +++ b/crates/stackable-versioned-macros/src/lib.rs @@ -22,15 +22,21 @@ mod consts; /// ## Declaring Versions /// /// It is **important** to note that this macro must be placed before any other -/// (derive) macros and attributes. This ensures that the macros and attributes -/// are applied to the generated versioned instances of the struct or enum. +/// (derive) macros and attributes. Macros supplied before the versioned macro +/// will be erased, because the original struct or enum (container) is erased, +/// and new containers are generated. This ensures that the macros and +/// attributes are applied to the generated versioned instances of the +/// container. /// /// Before any of the fields or variants can be versioned, versions need to be /// declared at the container level. Each version currently supports two /// parameters: `name` and the `deprecated` flag. The `name` must be a valid -/// (and supported) format - currently, only Kubernetes API versions are -/// supported. The macro checks each declared version and reports any error -/// encountered during parsing. +/// (and supported) format. +/// +///
+/// Currently, only Kubernetes API versions are supported. The macro checks each +/// declared version and reports any error encountered during parsing. +///
/// /// ``` /// # use stackable_versioned_macros::versioned; @@ -46,9 +52,9 @@ mod consts; /// 1. The `#[automatically_derived]` attribute indicates that the following /// piece of code is automatically generated by a macro instead of being /// handwritten by a developer. This information is used by cargo and rustc. -/// 2. For each declared version, a new module containing the struct or enum -/// (container) is generated. This enables you to reference the container by -/// versions via `v1alpha1::Foo`. +/// 2. For each declared version, a new module containing the container is +/// generated. This enables you to reference the container by versions via +/// `v1alpha1::Foo`. /// 3. This `use` statement gives the generated containers access to the imports /// at the top of the file. This is a convenience, because otherwise you /// would need to prefix used items with `super::`. Additionally, other @@ -117,8 +123,8 @@ mod consts; /// /// ## Item Actions /// -/// This library currently supports three different item actions. Items can -/// be added, renamed and deprecated. The macro ensures that these actions +/// This crate currently supports three different item actions. Items can +/// be added, changed, and deprecated. The macro ensures that these actions /// adhere to the following set of rules: /// /// 1. Items cannot be added and deprecated in the same version. @@ -366,15 +372,15 @@ mod consts; /// automatically generates `From` implementations. On a high level, code /// generated for two versions _a_ and _b_, with _a < b_ looks like this: /// `impl From
for b`. As you can see, only upgrading is currently supported. -/// Downgrading, so going from a higher version to a lower one, is not supported -/// at the moment. +/// Downgrading from a higher version to a lower one is not supported at the +/// moment. /// /// This automatic generation can be skipped to for example enable a custom /// implementation for more complex conversions. /// /// ### Skipping at the Container Level /// -/// Disabling this feature on the container level results in no `From` +/// Disabling this behavior at the container level results in no `From` /// implementation for all versions. /// /// ``` @@ -397,7 +403,7 @@ mod consts; /// /// ### Skipping at the Version Level /// -/// Disabling this feature at the version level results in no `From` +/// Disabling this behavior at the version level results in no `From` /// implementation for that particular version. This can be read as "skip /// generation for converting _this_ version to the next one". In the example /// below no conversion between version `v1beta1` and `v1` is generated. From 29aed25e43557bf5e845324cf63162b0e6a29d19 Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 12 Sep 2024 16:27:31 +0200 Subject: [PATCH 7/7] chore(docs): Adjust wording --- crates/stackable-versioned-macros/src/lib.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/stackable-versioned-macros/src/lib.rs b/crates/stackable-versioned-macros/src/lib.rs index 70e40dc67..ca18d81d5 100644 --- a/crates/stackable-versioned-macros/src/lib.rs +++ b/crates/stackable-versioned-macros/src/lib.rs @@ -368,15 +368,14 @@ mod consts; /// /// ## Auto-generated `From` Implementations /// -/// To enable smooth version upgrades of the same container, the macro -/// automatically generates `From` implementations. On a high level, code -/// generated for two versions _a_ and _b_, with _a < b_ looks like this: -/// `impl From for b`. As you can see, only upgrading is currently supported. -/// Downgrading from a higher version to a lower one is not supported at the -/// moment. +/// To enable smooth container version upgrades, the macro automatically +/// generates `From` implementations. On a high level, code generated for two +/// versions _a_ and _b_, with _a < b_ looks like this: `impl From for b`. +/// As you can see, only upgrading is currently supported. Downgrading from a +/// higher version to a lower one is not supported at the moment. /// -/// This automatic generation can be skipped to for example enable a custom -/// implementation for more complex conversions. +/// This automatic generation can be skipped to enable a custom implementation +/// for more complex conversions. /// /// ### Skipping at the Container Level /// @@ -428,7 +427,7 @@ mod consts; /// ## Kubernetes-specific Features /// /// This macro also offers support for Kubernetes-specific versioning, -/// especially for CustomResourceDefinitions, short CRDs. These features are +/// especially for CustomResourceDefinitions (CRDs). These features are /// completely opt-in. You need to enable the `k8s` feature (which enables /// optional dependencies) and use the `k8s()` parameter in the macro. ///