diff --git a/crates/stackable-versioned-macros/src/attrs/item/mod.rs b/crates/stackable-versioned-macros/src/attrs/item/mod.rs index 4c8e2dfcf..7b4092f52 100644 --- a/crates/stackable-versioned-macros/src/attrs/item/mod.rs +++ b/crates/stackable-versioned-macros/src/attrs/item/mod.rs @@ -8,7 +8,7 @@ use syn::{Attribute, Path, Type, spanned::Spanned}; use crate::{ codegen::{VersionDefinition, item::ItemStatus}, - utils::ItemIdents, + utils::{ItemIdents, doc_comments::DocComments as _}, }; mod field; @@ -223,9 +223,12 @@ impl CommonItemAttributes { let mut errors = Error::accumulator(); for change in &self.changes { - if change.from_name.is_none() && change.from_type.is_none() { + if change.from_name.is_none() + && change.from_type.is_none() + && change.from_docs.is_none() + { errors.push(Error::custom( - "both `from_name` and `from_type` are unset. Is this `changed()` action needed?" + "`from_name`, `from_type` and `from_docs` are unset. Is this `changed()` action needed?" ).with_span(&change.since.span())); } @@ -288,6 +291,26 @@ impl CommonItemAttributes { } impl CommonItemAttributes { + /// Returns a sparse map of the changed doc comments of the item, keyed by the version of the + /// change. + /// + /// Sparse in this context means that not every version is present as a key. Only versions + /// containing a doc change are included. This is explicitly different from + /// `VersionedField::changes` for which the code guarantees that there is an entry for all + /// versions. + // + // NOTE (@Techassi): Potentially roll this into the changeset below so that there will be a + // single sure of truth for field generation again. + pub fn changed_docs(&self) -> BTreeMap> { + self.changes + .iter() + .filter_map(|change| { + let docs = change.from_docs.as_deref()?; + Some((*change.since, docs.into_doc_comments())) + }) + .collect() + } + #[expect(clippy::too_many_lines)] pub fn into_changeset( self, @@ -458,11 +481,13 @@ fn default_default_fn() -> SpannedValue { /// - `changed(since = "...", from_name = "...", from_type="...")` /// - `changed(since = "...", from_name = "...", from_type="...", upgrade_with = "...")` /// - `changed(since = "...", from_name = "...", from_type="...", downgrade_with = "...")` +/// - `changed(since = "...", from_docs = "...")` #[derive(Clone, Debug, FromMeta)] pub struct ChangedAttributes { pub since: SpannedValue, pub from_name: Option>, pub from_type: Option>, + pub from_docs: Option>, pub upgrade_with: Option>, pub downgrade_with: Option>, } diff --git a/crates/stackable-versioned-macros/src/codegen/item/field.rs b/crates/stackable-versioned-macros/src/codegen/item/field.rs index ea074aeea..2b35f984c 100644 --- a/crates/stackable-versioned-macros/src/codegen/item/field.rs +++ b/crates/stackable-versioned-macros/src/codegen/item/field.rs @@ -11,7 +11,7 @@ use crate::{ codegen::{ Direction, VersionDefinition, changes::{BTreeMapExt, ChangesetExt}, - item::ItemStatus, + item::{ItemStatus, generate_attributes}, module::ModuleGenerationContext, }, utils::{ItemIdentExt, ItemIdents}, @@ -20,6 +20,16 @@ use crate::{ #[derive(Debug)] pub struct VersionedField { pub original_attributes: Vec, + + /// A sparse map of changed doc comments. + /// + /// This map being sparse means that access to it must be able to deal with a version not being + /// present. Using `unwrap()`, [`BTreeMapExt::get_expect`] or other similar functions is not + /// recommended. Also see `CommonItemAttributes::changed_docs` for further details. + // + // NOTE (@Techassi): Potentially roll this into the changeset below so that there will be a + // single sure of truth for field generation again. + pub changed_docs: BTreeMap>, pub changes: Option>, pub idents: FieldIdents, pub hint: Option, @@ -45,6 +55,7 @@ impl VersionedField { })?; let idents = FieldIdents::from(ident); + let changed_docs = field_attributes.common.changed_docs(); let changes = field_attributes .common .into_changeset(&idents, field.ty.clone()); @@ -53,6 +64,7 @@ impl VersionedField { Ok(Self { original_attributes: field_attributes.attrs, hint: field_attributes.hint, + changed_docs, ty: field.ty, changes, idents, @@ -82,7 +94,11 @@ impl VersionedField { /// } /// ``` pub fn generate_for_container(&self, version: &VersionDefinition) -> Option { - let original_attributes = &self.original_attributes; + let attributes = generate_attributes( + &self.original_attributes, + &self.changed_docs, + &version.inner, + ); #[allow(clippy::single_match_else)] match &self.changes { @@ -106,13 +122,13 @@ impl VersionedField { ) }) { ItemStatus::Addition { ident, ty, .. } => Some(quote! { - #(#original_attributes)* + #attributes pub #ident: #ty, }), ItemStatus::Change { to_ident, to_type, .. } => Some(quote! { - #(#original_attributes)* + #attributes pub #to_ident: #to_type, }), ItemStatus::Deprecation { @@ -133,7 +149,7 @@ impl VersionedField { }; Some(quote! { - #(#original_attributes)* + #attributes #deprecated_attr pub #field_ident: #field_type, }) @@ -149,7 +165,7 @@ impl VersionedField { let deprecated_attr = previously_deprecated.then(|| quote! {#[deprecated]}); Some(quote! { - #(#original_attributes)* + #attributes #deprecated_attr pub #ident: #ty, }) @@ -163,7 +179,7 @@ impl VersionedField { let field_type = &self.ty; Some(quote! { - #(#original_attributes)* + #attributes pub #field_ident: #field_type, }) } diff --git a/crates/stackable-versioned-macros/src/codegen/item/mod.rs b/crates/stackable-versioned-macros/src/codegen/item/mod.rs index 46abd7df8..9696b7adb 100644 --- a/crates/stackable-versioned-macros/src/codegen/item/mod.rs +++ b/crates/stackable-versioned-macros/src/codegen/item/mod.rs @@ -1,5 +1,12 @@ +use std::{collections::BTreeMap, ops::Bound}; + use darling::util::IdentString; -use syn::{Path, Type}; +use k8s_version::Version; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{Attribute, Meta, Path, Type}; + +use crate::codegen::changes::Neighbors as _; mod field; pub use field::*; @@ -7,6 +14,31 @@ pub use field::*; mod variant; pub use variant::*; +/// Generates the attributes of an item (field or variant) for the provided `version`. +/// +/// If the docs of the item are changed in a later version (via `from_docs`), the original doc +/// comments are replaced by the docs which are valid in `version`. +pub fn generate_attributes( + original_attributes: &[Attribute], + changed_docs: &BTreeMap>, + version: &Version, +) -> TokenStream { + // The docs valid in this version are the previous docs of the closest change after this + // version. If there is no such change, the original docs are still valid. + let Some((_, docs)) = changed_docs.up_bound(Bound::Excluded(version)) else { + return quote! { #(#original_attributes)* }; + }; + + let attributes = original_attributes.iter().filter(|attribute| { + !matches!(&attribute.meta, Meta::NameValue(name_value) if name_value.path.is_ident("doc")) + }); + + quote! { + #(#[doc = #docs])* + #(#attributes)* + } +} + #[derive(Debug, PartialEq, Eq)] pub enum ItemStatus { Addition { diff --git a/crates/stackable-versioned-macros/src/codegen/item/variant.rs b/crates/stackable-versioned-macros/src/codegen/item/variant.rs index 0175b43aa..d97e10c1b 100644 --- a/crates/stackable-versioned-macros/src/codegen/item/variant.rs +++ b/crates/stackable-versioned-macros/src/codegen/item/variant.rs @@ -13,13 +13,14 @@ use crate::{ codegen::{ Direction, VersionDefinition, changes::{BTreeMapExt, ChangesetExt}, - item::ItemStatus, + item::{ItemStatus, generate_attributes}, }, utils::ItemIdents, }; pub struct VersionedVariant { pub original_attributes: Vec, + pub changed_docs: BTreeMap>, pub changes: Option>, pub idents: VariantIdents, pub fields: Fields, @@ -39,10 +40,12 @@ impl VersionedVariant { attrs: Vec::new(), bang_token: Not([Span::call_site()]), }); + let changed_docs = variant_attributes.common.changed_docs(); let changes = variant_attributes.common.into_changeset(&idents, ty); Ok(Self { original_attributes: variant_attributes.attrs, + changed_docs, fields: variant.fields, idents, changes, @@ -63,7 +66,11 @@ impl VersionedVariant { /// Generates tokens to be used in a container definition. pub fn generate_for_container(&self, version: &VersionDefinition) -> Option { - let original_attributes = &self.original_attributes; + let attributes = generate_attributes( + &self.original_attributes, + &self.changed_docs, + &version.inner, + ); let fields = &self.fields; #[allow(clippy::single_match_else)] @@ -80,11 +87,11 @@ impl VersionedVariant { ) }) { ItemStatus::Addition { ident, .. } => Some(quote! { - #(#original_attributes)* + #attributes #ident #fields, }), ItemStatus::Change { to_ident, .. } => Some(quote! { - #(#original_attributes)* + #attributes #to_ident #fields, }), ItemStatus::Deprecation { ident, note, .. } => { @@ -103,7 +110,7 @@ impl VersionedVariant { }; Some(quote! { - #(#original_attributes)* + #attributes #deprecated_attr #ident #fields, }) @@ -118,7 +125,7 @@ impl VersionedVariant { let deprecated_attr = previously_deprecated.then(|| quote! {#[deprecated]}); Some(quote! { - #(#original_attributes)* + #attributes #deprecated_attr #ident #fields, }) @@ -132,7 +139,7 @@ impl VersionedVariant { let ident = &self.idents.original; Some(quote! { - #(#original_attributes)* + #attributes #ident #fields, }) } diff --git a/crates/stackable-versioned-macros/src/lib.rs b/crates/stackable-versioned-macros/src/lib.rs index 4e666a6b2..6813c61d2 100644 --- a/crates/stackable-versioned-macros/src/lib.rs +++ b/crates/stackable-versioned-macros/src/lib.rs @@ -490,12 +490,14 @@ mod utils; /// ### 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 arguments are: +/// combines renames, type changes and doc comment changes into a single action. +/// You can choose to change the name, change the type, change the doc comment +/// or combine multiple changes. Available arguments 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. +/// - `from_docs` to provide the previous doc comments of the item. /// - `upgrade_with` to provide a custom upgrade function. This argument can /// only be used in combination with the `from_type` argument. The expected /// function signature is: `fn (OLD_TYPE) -> NEW_TYPE`. This function must @@ -561,6 +563,58 @@ mod utils; /// ``` /// /// +/// #### Changed Docs +/// +/// The doc comments of an item can be changed using the `from_docs` argument. +/// The doc comments attached to the item are used after the version of the +/// last relevant change. All earlier versions use the doc comments provided via +/// `from_docs` instead. This is especially useful to adjust the description of +/// a field in the generated CRD schema without changing it for older versions. +/// +/// ``` +/// # use stackable_versioned_macros::versioned; +/// #[versioned(version(name = "v1alpha1"), version(name = "v1beta1"))] +/// mod versioned { +/// pub struct Foo { +/// /// The number of bars. +/// #[versioned(changed(since = "v1beta1", from_docs = "The bar."))] +/// bar: usize, +/// baz: bool, +/// } +/// } +/// ``` +/// +///
+/// Expand Generated Code +/// +/// 1. In version `v1alpha1` the field uses the doc comments provided via +/// `from_docs`. +/// 2. In the next version, `v1beta1`, the field uses the doc comments attached +/// to the field. +/// +/// ```ignore +/// pub mod v1alpha1 { +/// use super::*; +/// pub struct Foo { +/// /// The bar. // 1 +/// pub bar: usize, +/// pub baz: bool, +/// } +/// } +/// +/// // Snip +/// +/// pub mod v1beta1 { +/// use super::*; +/// pub struct Foo { +/// /// The number of bars. // 2 +/// pub bar: usize, +/// pub baz: bool, +/// } +/// } +/// ``` +///
+/// /// ### Deprecated Action /// /// This action indicates that an item is deprecated in a particular version. diff --git a/crates/stackable-versioned-macros/src/utils/doc_comments.rs b/crates/stackable-versioned-macros/src/utils/doc_comments.rs index 6bd8b5c96..f4c06ae9f 100644 --- a/crates/stackable-versioned-macros/src/utils/doc_comments.rs +++ b/crates/stackable-versioned-macros/src/utils/doc_comments.rs @@ -12,12 +12,26 @@ impl DocComments for &str { .trim() .lines() // Trim the leading and trailing whitespace on each line that can be - // introduced when the developer indents multi-line comments. - .map(|line| line.trim().to_owned()) + // introduced when the developer indents multi-line comments. We then + // re-add a leading space to be in line with all other, non-generated + // dod comments. + .map(|line| format!(" {line}", line = line.trim())) .collect() } } +impl DocComments for String { + fn into_doc_comments(self) -> Vec { + self.as_str().into_doc_comments() + } +} + +impl DocComments for &String { + fn into_doc_comments(self) -> Vec { + self.as_str().into_doc_comments() + } +} + impl DocComments for Option<&str> { fn into_doc_comments(self) -> Vec { self.map_or(vec![], DocComments::into_doc_comments) diff --git a/crates/stackable-versioned-macros/tests/inputs/fail/changed.stderr b/crates/stackable-versioned-macros/tests/inputs/fail/changed.stderr index 61d10138f..417ca1250 100644 --- a/crates/stackable-versioned-macros/tests/inputs/fail/changed.stderr +++ b/crates/stackable-versioned-macros/tests/inputs/fail/changed.stderr @@ -10,7 +10,7 @@ error: the previous name must not start with the deprecation prefix 13 | changed(since = "v1", from_name = "deprecated_baz"), | ^^^^^^^^^^^^^^^^ -error: both `from_name` and `from_type` are unset. Is this `changed()` action needed? +error: `from_name`, `from_type` and `from_docs` are unset. Is this `changed()` action needed? --> tests/inputs/fail/changed.rs:14:29 | 14 | changed(since = "v2") diff --git a/crates/stackable-versioned-macros/tests/inputs/pass/docs.rs b/crates/stackable-versioned-macros/tests/inputs/pass/docs.rs index 23888ab90..13f7c8d11 100644 --- a/crates/stackable-versioned-macros/tests/inputs/pass/docs.rs +++ b/crates/stackable-versioned-macros/tests/inputs/pass/docs.rs @@ -18,7 +18,7 @@ use stackable_versioned::versioned; // --- mod versioned { /// Test - #[derive(Default)] + #[derive(Default, serde::Deserialize)] struct Foo { /// This field is available in every version (so far). foo: String, @@ -35,6 +35,39 @@ mod versioned { #[versioned(changed(since = "v1beta1", from_name = "qoox"))] #[versioned(changed(since = "v1", from_name = "qaax"))] quux: String, + + /// The docs of this field changed in v1beta1 and v2. + #[versioned( + changed(since = "v1beta1", from_docs = "These are the docs in v1alpha1."), + changed( + since = "v2", + from_docs = r#" + These are the docs from v1beta1 until v1. + + Multi-line docs are also supported. + "# + ) + )] + // Those must be preserved. + #[doc(alias = "grault")] + #[serde(rename = "corge")] + corge: String, + + /// The docs of this field changed in v1, while it was renamed in v1beta1. + #[versioned( + changed(since = "v1beta1", from_name = "waldo"), + changed(since = "v1", from_docs = "These are the docs before v1.") + )] + fred: String, + } + + /// Test + #[derive(Default)] + enum Bar { + /// The docs of this variant changed in v1. + #[versioned(changed(since = "v1", from_docs = "These are the docs before v1."))] + #[default] + Baz, } } // --- diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@basic.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@basic.rs.snap index a14fda07e..7668bc375 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@basic.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@basic.rs.snap @@ -180,13 +180,13 @@ impl Foo { stored_apiversion.as_version_str(), ) } - ///Tries to convert a list of objects of kind [`Foo`] to the desired API version - ///specified in the [`ConversionReview`][cr]. + /// Tries to convert a list of objects of kind [`Foo`] to the desired API version + /// specified in the [`ConversionReview`][cr]. /// - ///The returned [`ConversionReview`][cr] either indicates a success or a failure, which - ///is handed back to the Kubernetes API server. + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. /// - ///[cr]: ::kube::core::conversion::ConversionReview + /// [cr]: ::kube::core::conversion::ConversionReview pub fn try_convert( review: ::kube::core::conversion::ConversionReview, ) -> ::kube::core::conversion::ConversionReview { diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking.rs.snap index 57f5cf4ae..4462b74b3 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking.rs.snap @@ -224,13 +224,13 @@ impl Foo { stored_apiversion.as_version_str(), ) } - ///Tries to convert a list of objects of kind [`Foo`] to the desired API version - ///specified in the [`ConversionReview`][cr]. + /// Tries to convert a list of objects of kind [`Foo`] to the desired API version + /// specified in the [`ConversionReview`][cr]. /// - ///The returned [`ConversionReview`][cr] either indicates a success or a failure, which - ///is handed back to the Kubernetes API server. + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. /// - ///[cr]: ::kube::core::conversion::ConversionReview + /// [cr]: ::kube::core::conversion::ConversionReview pub fn try_convert( review: ::kube::core::conversion::ConversionReview, ) -> ::kube::core::conversion::ConversionReview { diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@crate_overrides.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@crate_overrides.rs.snap index 03cbedf1f..b2abee3ab 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@crate_overrides.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@crate_overrides.rs.snap @@ -167,13 +167,13 @@ impl Foo { stored_apiversion.as_version_str(), ) } - ///Tries to convert a list of objects of kind [`Foo`] to the desired API version - ///specified in the [`ConversionReview`][cr]. + /// Tries to convert a list of objects of kind [`Foo`] to the desired API version + /// specified in the [`ConversionReview`][cr]. /// - ///The returned [`ConversionReview`][cr] either indicates a success or a failure, which - ///is handed back to the Kubernetes API server. + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. /// - ///[cr]: ::kube::core::conversion::ConversionReview + /// [cr]: ::kube::core::conversion::ConversionReview pub fn try_convert( review: ::kube::core::conversion::ConversionReview, ) -> ::kube::core::conversion::ConversionReview { diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@crate_overrides_only_kube.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@crate_overrides_only_kube.rs.snap index 5a3a81261..b20b1a2c9 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@crate_overrides_only_kube.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@crate_overrides_only_kube.rs.snap @@ -167,13 +167,13 @@ impl Foo { stored_apiversion.as_version_str(), ) } - ///Tries to convert a list of objects of kind [`Foo`] to the desired API version - ///specified in the [`ConversionReview`][cr]. + /// Tries to convert a list of objects of kind [`Foo`] to the desired API version + /// specified in the [`ConversionReview`][cr]. /// - ///The returned [`ConversionReview`][cr] either indicates a success or a failure, which - ///is handed back to the Kubernetes API server. + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. /// - ///[cr]: ::kube::core::conversion::ConversionReview + /// [cr]: ::kube::core::conversion::ConversionReview pub fn try_convert( review: ::kube::core::conversion::ConversionReview, ) -> ::kube::core::conversion::ConversionReview { diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@crd_doc.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@crd_doc.rs.snap index 54b5f73f6..a238967b3 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@crd_doc.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@crd_doc.rs.snap @@ -41,13 +41,13 @@ impl Foo { stored_apiversion.as_version_str(), ) } - ///Tries to convert a list of objects of kind [`Foo`] to the desired API version - ///specified in the [`ConversionReview`][cr]. + /// Tries to convert a list of objects of kind [`Foo`] to the desired API version + /// specified in the [`ConversionReview`][cr]. /// - ///The returned [`ConversionReview`][cr] either indicates a success or a failure, which - ///is handed back to the Kubernetes API server. + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. /// - ///[cr]: ::kube::core::conversion::ConversionReview + /// [cr]: ::kube::core::conversion::ConversionReview pub fn try_convert( review: ::kube::core::conversion::ConversionReview, ) -> ::kube::core::conversion::ConversionReview { diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@docs.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@docs.rs.snap index d37c68374..e79b8d287 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@docs.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@docs.rs.snap @@ -7,7 +7,7 @@ input_file: crates/stackable-versioned-macros/tests/inputs/pass/docs.rs mod v1alpha1 { use super::*; /// Test - #[derive(Default)] + #[derive(Default, serde::Deserialize)] pub struct Foo { /// This field is available in every version (so far). pub foo: String, @@ -15,6 +15,19 @@ mod v1alpha1 { pub bar: String, /// This is will keep changing over time. pub qoox: String, + /// These are the docs in v1alpha1. + #[doc(alias = "grault")] + #[serde(rename = "corge")] + pub corge: String, + /// These are the docs before v1. + pub waldo: String, + } + /// Test + #[derive(Default)] + pub enum Bar { + /// These are the docs before v1. + #[default] + Baz, } } #[automatically_derived] @@ -31,6 +44,8 @@ where deprecated_bar: __sv_foo.bar.into(), baz: ::std::default::Default::default(), qaax: __sv_foo.qoox.into(), + corge: __sv_foo.corge.into(), + fred: __sv_foo.waldo.into(), }; if let Some(upgrades) = status.changes().upgrades.remove(&"v1beta1".to_owned()) { for ::stackable_versioned::ChangedValue { json_path, value } in upgrades { @@ -68,18 +83,36 @@ where foo: __sv_foo.foo.into(), bar: __sv_foo.deprecated_bar.into(), qoox: __sv_foo.qaax.into(), + corge: __sv_foo.corge.into(), + waldo: __sv_foo.fred.into(), }; spec } } #[automatically_derived] +impl ::core::convert::From for v1beta1::Bar { + fn from(__sv_bar: v1alpha1::Bar) -> Self { + match __sv_bar { + v1alpha1::Bar::Baz => v1beta1::Bar::Baz, + } + } +} +#[automatically_derived] +impl ::core::convert::From for v1alpha1::Bar { + fn from(__sv_bar: v1beta1::Bar) -> Self { + match __sv_bar { + v1beta1::Bar::Baz => v1alpha1::Bar::Baz, + } + } +} +#[automatically_derived] mod v1beta1 { use super::*; - ///Additional docs for this version which are purposefully long to - ///show how manual line wrapping works. \ - ///Multi-line docs are also supported, as per regular doc-comments. + /// Additional docs for this version which are purposefully long to + /// show how manual line wrapping works. \ + /// Multi-line docs are also supported, as per regular doc-comments. /// Test - #[derive(Default)] + #[derive(Default, serde::Deserialize)] pub struct Foo { /// This field is available in every version (so far). pub foo: String, @@ -90,6 +123,24 @@ mod v1beta1 { pub baz: String, /// This is will keep changing over time. pub qaax: String, + /// These are the docs from v1beta1 until v1. + /// + /// Multi-line docs are also supported. + #[doc(alias = "grault")] + #[serde(rename = "corge")] + pub corge: String, + /// These are the docs before v1. + pub fred: String, + } + /// Additional docs for this version which are purposefully long to + /// show how manual line wrapping works. \ + /// Multi-line docs are also supported, as per regular doc-comments. + /// Test + #[derive(Default)] + pub enum Bar { + /// These are the docs before v1. + #[default] + Baz, } } #[automatically_derived] @@ -105,6 +156,8 @@ where deprecated_bar: __sv_foo.deprecated_bar.into(), baz: __sv_foo.baz.into(), qaax: __sv_foo.qaax.into(), + corge: __sv_foo.corge.into(), + fred: __sv_foo.fred.into(), }; spec } @@ -122,15 +175,33 @@ where deprecated_bar: __sv_foo.deprecated_bar.into(), baz: __sv_foo.baz.into(), qaax: __sv_foo.qaax.into(), + corge: __sv_foo.corge.into(), + fred: __sv_foo.fred.into(), }; spec } } #[automatically_derived] +impl ::core::convert::From for v1beta2::Bar { + fn from(__sv_bar: v1beta1::Bar) -> Self { + match __sv_bar { + v1beta1::Bar::Baz => v1beta2::Bar::Baz, + } + } +} +#[automatically_derived] +impl ::core::convert::From for v1beta1::Bar { + fn from(__sv_bar: v1beta2::Bar) -> Self { + match __sv_bar { + v1beta2::Bar::Baz => v1beta1::Bar::Baz, + } + } +} +#[automatically_derived] mod v1beta2 { use super::*; /// Test - #[derive(Default)] + #[derive(Default, serde::Deserialize)] pub struct Foo { /// This field is available in every version (so far). pub foo: String, @@ -141,6 +212,21 @@ mod v1beta2 { pub baz: String, /// This is will keep changing over time. pub qaax: String, + /// These are the docs from v1beta1 until v1. + /// + /// Multi-line docs are also supported. + #[doc(alias = "grault")] + #[serde(rename = "corge")] + pub corge: String, + /// These are the docs before v1. + pub fred: String, + } + /// Test + #[derive(Default)] + pub enum Bar { + /// These are the docs before v1. + #[default] + Baz, } } #[automatically_derived] @@ -156,6 +242,8 @@ where deprecated_bar: __sv_foo.deprecated_bar.into(), baz: __sv_foo.baz.into(), quux: __sv_foo.qaax.into(), + corge: __sv_foo.corge.into(), + fred: __sv_foo.fred.into(), }; spec } @@ -173,15 +261,33 @@ where deprecated_bar: __sv_foo.deprecated_bar.into(), baz: __sv_foo.baz.into(), qaax: __sv_foo.quux.into(), + corge: __sv_foo.corge.into(), + fred: __sv_foo.fred.into(), }; spec } } #[automatically_derived] +impl ::core::convert::From for v1::Bar { + fn from(__sv_bar: v1beta2::Bar) -> Self { + match __sv_bar { + v1beta2::Bar::Baz => v1::Bar::Baz, + } + } +} +#[automatically_derived] +impl ::core::convert::From for v1beta2::Bar { + fn from(__sv_bar: v1::Bar) -> Self { + match __sv_bar { + v1::Bar::Baz => v1beta2::Bar::Baz, + } + } +} +#[automatically_derived] mod v1 { use super::*; /// Test - #[derive(Default)] + #[derive(Default, serde::Deserialize)] pub struct Foo { /// This field is available in every version (so far). pub foo: String, @@ -192,6 +298,21 @@ mod v1 { pub baz: String, /// This is will keep changing over time. pub quux: String, + /// These are the docs from v1beta1 until v1. + /// + /// Multi-line docs are also supported. + #[doc(alias = "grault")] + #[serde(rename = "corge")] + pub corge: String, + /// The docs of this field changed in v1, while it was renamed in v1beta1. + pub fred: String, + } + /// Test + #[derive(Default)] + pub enum Bar { + /// The docs of this variant changed in v1. + #[default] + Baz, } } #[automatically_derived] @@ -207,6 +328,8 @@ where deprecated_bar: __sv_foo.deprecated_bar.into(), baz: __sv_foo.baz.into(), quux: __sv_foo.quux.into(), + corge: __sv_foo.corge.into(), + fred: __sv_foo.fred.into(), }; spec } @@ -224,15 +347,33 @@ where deprecated_bar: __sv_foo.deprecated_bar.into(), baz: __sv_foo.baz.into(), quux: __sv_foo.quux.into(), + corge: __sv_foo.corge.into(), + fred: __sv_foo.fred.into(), }; spec } } #[automatically_derived] +impl ::core::convert::From for v2::Bar { + fn from(__sv_bar: v1::Bar) -> Self { + match __sv_bar { + v1::Bar::Baz => v2::Bar::Baz, + } + } +} +#[automatically_derived] +impl ::core::convert::From for v1::Bar { + fn from(__sv_bar: v2::Bar) -> Self { + match __sv_bar { + v2::Bar::Baz => v1::Bar::Baz, + } + } +} +#[automatically_derived] mod v2 { use super::*; /// Test - #[derive(Default)] + #[derive(Default, serde::Deserialize)] pub struct Foo { /// This field is available in every version (so far). pub foo: String, @@ -243,5 +384,18 @@ mod v2 { pub baz: String, /// This is will keep changing over time. pub quux: String, + /// The docs of this field changed in v1beta1 and v2. + #[doc(alias = "grault")] + #[serde(rename = "corge")] + pub corge: String, + /// The docs of this field changed in v1, while it was renamed in v1beta1. + pub fred: String, + } + /// Test + #[derive(Default)] + pub enum Bar { + /// The docs of this variant changed in v1. + #[default] + Baz, } } diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@module.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@module.rs.snap index 16eeb2106..1ca637e89 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@module.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@module.rs.snap @@ -383,13 +383,13 @@ impl Foo { stored_apiversion.as_version_str(), ) } - ///Tries to convert a list of objects of kind [`Foo`] to the desired API version - ///specified in the [`ConversionReview`][cr]. + /// Tries to convert a list of objects of kind [`Foo`] to the desired API version + /// specified in the [`ConversionReview`][cr]. /// - ///The returned [`ConversionReview`][cr] either indicates a success or a failure, which - ///is handed back to the Kubernetes API server. + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. /// - ///[cr]: ::kube::core::conversion::ConversionReview + /// [cr]: ::kube::core::conversion::ConversionReview pub fn try_convert( review: ::kube::core::conversion::ConversionReview, ) -> ::kube::core::conversion::ConversionReview { @@ -689,13 +689,13 @@ impl Bar { stored_apiversion.as_version_str(), ) } - ///Tries to convert a list of objects of kind [`Bar`] to the desired API version - ///specified in the [`ConversionReview`][cr]. + /// Tries to convert a list of objects of kind [`Bar`] to the desired API version + /// specified in the [`ConversionReview`][cr]. /// - ///The returned [`ConversionReview`][cr] either indicates a success or a failure, which - ///is handed back to the Kubernetes API server. + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. /// - ///[cr]: ::kube::core::conversion::ConversionReview + /// [cr]: ::kube::core::conversion::ConversionReview pub fn try_convert( review: ::kube::core::conversion::ConversionReview, ) -> ::kube::core::conversion::ConversionReview { diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@module_preserve.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@module_preserve.rs.snap index bcf1f5ef0..6478610a9 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@module_preserve.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@module_preserve.rs.snap @@ -356,13 +356,13 @@ pub(crate) mod versioned { stored_apiversion.as_version_str(), ) } - ///Tries to convert a list of objects of kind [`Foo`] to the desired API version - ///specified in the [`ConversionReview`][cr]. + /// Tries to convert a list of objects of kind [`Foo`] to the desired API version + /// specified in the [`ConversionReview`][cr]. /// - ///The returned [`ConversionReview`][cr] either indicates a success or a failure, which - ///is handed back to the Kubernetes API server. + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. /// - ///[cr]: ::kube::core::conversion::ConversionReview + /// [cr]: ::kube::core::conversion::ConversionReview pub fn try_convert( review: ::kube::core::conversion::ConversionReview, ) -> ::kube::core::conversion::ConversionReview { @@ -657,13 +657,13 @@ pub(crate) mod versioned { stored_apiversion.as_version_str(), ) } - ///Tries to convert a list of objects of kind [`Bar`] to the desired API version - ///specified in the [`ConversionReview`][cr]. + /// Tries to convert a list of objects of kind [`Bar`] to the desired API version + /// specified in the [`ConversionReview`][cr]. /// - ///The returned [`ConversionReview`][cr] either indicates a success or a failure, which - ///is handed back to the Kubernetes API server. + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. /// - ///[cr]: ::kube::core::conversion::ConversionReview + /// [cr]: ::kube::core::conversion::ConversionReview pub fn try_convert( review: ::kube::core::conversion::ConversionReview, ) -> ::kube::core::conversion::ConversionReview { diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@renamed_kind.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@renamed_kind.rs.snap index 83968392c..d19138b94 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@renamed_kind.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@renamed_kind.rs.snap @@ -167,13 +167,13 @@ impl FooBar { stored_apiversion.as_version_str(), ) } - ///Tries to convert a list of objects of kind [`FooBar`] to the desired API version - ///specified in the [`ConversionReview`][cr]. + /// Tries to convert a list of objects of kind [`FooBar`] to the desired API version + /// specified in the [`ConversionReview`][cr]. /// - ///The returned [`ConversionReview`][cr] either indicates a success or a failure, which - ///is handed back to the Kubernetes API server. + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. /// - ///[cr]: ::kube::core::conversion::ConversionReview + /// [cr]: ::kube::core::conversion::ConversionReview pub fn try_convert( review: ::kube::core::conversion::ConversionReview, ) -> ::kube::core::conversion::ConversionReview { diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@scale.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@scale.rs.snap index f05fdf09e..0f136f4e8 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@scale.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@scale.rs.snap @@ -49,13 +49,13 @@ impl Foo { stored_apiversion.as_version_str(), ) } - ///Tries to convert a list of objects of kind [`Foo`] to the desired API version - ///specified in the [`ConversionReview`][cr]. + /// Tries to convert a list of objects of kind [`Foo`] to the desired API version + /// specified in the [`ConversionReview`][cr]. /// - ///The returned [`ConversionReview`][cr] either indicates a success or a failure, which - ///is handed back to the Kubernetes API server. + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. /// - ///[cr]: ::kube::core::conversion::ConversionReview + /// [cr]: ::kube::core::conversion::ConversionReview pub fn try_convert( review: ::kube::core::conversion::ConversionReview, ) -> ::kube::core::conversion::ConversionReview { diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@shortnames.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@shortnames.rs.snap index c7a3b584e..2d6d012ee 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@shortnames.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@shortnames.rs.snap @@ -43,13 +43,13 @@ impl Foo { stored_apiversion.as_version_str(), ) } - ///Tries to convert a list of objects of kind [`Foo`] to the desired API version - ///specified in the [`ConversionReview`][cr]. + /// Tries to convert a list of objects of kind [`Foo`] to the desired API version + /// specified in the [`ConversionReview`][cr]. /// - ///The returned [`ConversionReview`][cr] either indicates a success or a failure, which - ///is handed back to the Kubernetes API server. + /// The returned [`ConversionReview`][cr] either indicates a success or a failure, which + /// is handed back to the Kubernetes API server. /// - ///[cr]: ::kube::core::conversion::ConversionReview + /// [cr]: ::kube::core::conversion::ConversionReview pub fn try_convert( review: ::kube::core::conversion::ConversionReview, ) -> ::kube::core::conversion::ConversionReview { diff --git a/crates/stackable-versioned/CHANGELOG.md b/crates/stackable-versioned/CHANGELOG.md index 3cf01678b..0a5a39fbf 100644 --- a/crates/stackable-versioned/CHANGELOG.md +++ b/crates/stackable-versioned/CHANGELOG.md @@ -6,11 +6,13 @@ All notable changes to this project will be documented in this file. ### Added +- Add `from_docs` argument to the `changed()` action to provide the previous doc comments of an item ([#1293]). - Add `#[versioned(hint(map))]` to provide a hint for map types during conversion ([#1285]). - Emit generics in plain `From` impl ([#1284]). [#1284]: https://github.com/stackabletech/operator-rs/pull/1284 [#1285]: https://github.com/stackabletech/operator-rs/pull/1285 +[#1293]: https://github.com/stackabletech/operator-rs/pull/1293 ## [0.11.1] - 2026-07-06 diff --git a/deny.toml b/deny.toml index 8ec7e4528..cc96f58e1 100644 --- a/deny.toml +++ b/deny.toml @@ -38,6 +38,19 @@ ignore = [ # # This can only be removed again if we decide to use a different crate. "RUSTSEC-2024-0436", + + # https://rustsec.org/advisories/RUSTSEC-2024-0449 + # The https://github.com/dtolnay/unsafe-libyaml repository was archived by + # its owner on 2024-03-25 and is read-only. The last release is 0.2.11 + # (March 2024). + # + # `unsafe-libyaml` is a machine translation of libyaml's C code into unsafe + # Rust (c2rust). Bugs found in it will not be fixed upstream. It is used by + # `serde_yaml` (itself deprecated) and by forks such as `serde_yaml_ng`. + # + # This can only be removed again once we decide which alternative YAML crate we want to migrate + # to. Currently, we favor `serde-saphyr`. + "RUSTSEC-2024-0449", ] [bans]