diff --git a/Cargo.lock b/Cargo.lock index a662ad0b7..0fb49ca5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3379,9 +3379,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.15" +version = "0.103.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281" dependencies = [ "ring", "rustls-pki-types", diff --git a/crates/stackable-versioned-macros/src/codegen/container/struct/conversion.rs b/crates/stackable-versioned-macros/src/codegen/container/struct/conversion.rs index 419e239fe..316b01a74 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/struct/conversion.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/struct/conversion.rs @@ -189,7 +189,7 @@ impl Struct { { fn tracking_from(#from_struct_ident: #from_module_ident::#struct_ident #type_generics, status: &mut __SvStatus, parent: &str) -> Self { // TODO (@Techassi): Only emit this if any of the fields below need it - use #versioned_path::TrackingInto as _; + use #versioned_path::{ExtractIfExt as _, TrackingInto as _}; #json_paths // Depending on the direction, we need to either insert changed values into @@ -264,7 +264,7 @@ impl Struct { return None; } - let match_arms: TokenStream = self + let removals: TokenStream = self .fields .iter() .filter_map(|f| f.generate_for_status_removal(direction, next_version, mod_gen_ctx)) @@ -273,16 +273,13 @@ impl Struct { match direction { Direction::Upgrade => { let next_version_string = next_version.inner.to_string(); - let versioned_path = &*mod_gen_ctx.crates.versioned; Some(quote! { - // NOTE (@Techassi): This is an awkward thing to do. Can we possibly use &str for the keys here? - if let Some(upgrades) = status.changes().upgrades.remove(&#next_version_string.to_owned()) { - for #versioned_path::ChangedValue { json_path, value } in upgrades { - match json_path { - #match_arms - _ => unreachable!(), - } + if let Some(upgrades) = status.changes().upgrades.get_mut(#next_version_string) { + #removals + + if upgrades.is_empty() { + status.changes().upgrades.remove(#next_version_string); } } }) diff --git a/crates/stackable-versioned-macros/src/codegen/item/field.rs b/crates/stackable-versioned-macros/src/codegen/item/field.rs index c14fcd92d..17ee2beb1 100644 --- a/crates/stackable-versioned-macros/src/codegen/item/field.rs +++ b/crates/stackable-versioned-macros/src/codegen/item/field.rs @@ -350,6 +350,7 @@ impl VersionedField { let changes = self.changes.as_ref()?; let yaml_serde_path = &*mod_gen_ctx.crates.yaml_serde; + let versioned_path = &*mod_gen_ctx.crates.versioned; match direction { Direction::Upgrade => { @@ -360,11 +361,15 @@ impl VersionedField { // we only need to generate code if the next change is "Addition". ItemStatus::Addition { ident, .. } => { let json_path_ident = ident.json_path_ident(); + let expect_message = format!( + "internal error: {{{json_path_ident}}} must be serializable for spec.{ident}" + ); Some(quote! { - json_path if json_path == #json_path_ident => { - spec.#ident = #yaml_serde_path::from_value(value).unwrap(); - }, + if let Some(#versioned_path::ChangedValue { value, .. }) = upgrades.extract_first_if(|u| u.json_path == #json_path_ident) { + // TODO (@Techassi): Switch to serde_json + spec.#ident = #yaml_serde_path::from_value(value).expect(#expect_message); + } }) } _ => None, 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 54b85a93d..348dac6f8 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 @@ -59,20 +59,23 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_bah_path = ::stackable_versioned::jthong_path(parent, "bah"); let mut spec = Self { bah: ::std::default::Default::default(), baz: __sv_foospec.baz.into(), }; - if let Some(upgrades) = status.changes().upgrades.remove(&"v1beta1".to_owned()) { - for ::stackable_versioned::ChangedValue { json_path, value } in upgrades { - match json_path { - json_path if json_path == __sv_bah_path => { - spec.bah = ::yaml_serde::from_value(value).unwrap(); - } - _ => unreachable!(), - } + if let Some(upgrades) = status.changes().upgrades.get_mut("v1beta1") { + if let Some(::stackable_versioned::ChangedValue { value, .. }) = upgrades + .extract_first_if(|u| u.json_path == __sv_bah_path) + { + spec.bah = ::yaml_serde::from_value(value) + .expect( + "internal error: {__sv_bah_path} must be serializable for spec.bah", + ); + } + if upgrades.is_empty() { + status.changes().upgrades.remove("v1beta1"); } } spec @@ -89,7 +92,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_bah_path = ::stackable_versioned::jthong_path(parent, "bah"); let upgrades = status .changes() @@ -164,7 +167,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let mut spec = Self { bar: __sv_foospec.bah.into(), baz: __sv_foospec.baz.into(), @@ -183,7 +186,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let mut spec = Self { bah: __sv_foospec.bar.into(), baz: __sv_foospec.baz.into(), diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_enum.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_enum.rs.snap index 42addbd12..ef0ba4fd1 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_enum.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_enum.rs.snap @@ -72,7 +72,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_connector_path = ::stackable_versioned::jthong_path( parent, "connector", @@ -94,7 +94,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_connector_path = ::stackable_versioned::jthong_path( parent, "connector", @@ -224,7 +224,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_rest_catalog_uri_path = ::stackable_versioned::jthong_path( parent, "rest_catalog_uri", @@ -233,15 +233,17 @@ where metastore: __sv_icebergconnector.metastore.into(), rest_catalog_uri: ::std::default::Default::default(), }; - if let Some(upgrades) = status.changes().upgrades.remove(&"v1alpha2".to_owned()) - { - for ::stackable_versioned::ChangedValue { json_path, value } in upgrades { - match json_path { - json_path if json_path == __sv_rest_catalog_uri_path => { - spec.rest_catalog_uri = ::yaml_serde::from_value(value).unwrap(); - } - _ => unreachable!(), - } + if let Some(upgrades) = status.changes().upgrades.get_mut("v1alpha2") { + if let Some(::stackable_versioned::ChangedValue { value, .. }) = upgrades + .extract_first_if(|u| u.json_path == __sv_rest_catalog_uri_path) + { + spec.rest_catalog_uri = ::yaml_serde::from_value(value) + .expect( + "internal error: {__sv_rest_catalog_uri_path} must be serializable for spec.rest_catalog_uri", + ); + } + if upgrades.is_empty() { + status.changes().upgrades.remove("v1alpha2"); } } spec @@ -260,7 +262,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_rest_catalog_uri_path = ::stackable_versioned::jthong_path( parent, "rest_catalog_uri", diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_generics.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_generics.rs.snap index d20e46bbc..5782cc0d4 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_generics.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_generics.rs.snap @@ -68,21 +68,24 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_bah_path = ::stackable_versioned::jthong_path(parent, "bah"); let __sv_baz_path = ::stackable_versioned::jthong_path(parent, "baz"); let mut spec = Self { bah: ::std::default::Default::default(), baz: __sv_foospec.baz.tracking_into(status, &__sv_baz_path), }; - if let Some(upgrades) = status.changes().upgrades.remove(&"v1beta1".to_owned()) { - for ::stackable_versioned::ChangedValue { json_path, value } in upgrades { - match json_path { - json_path if json_path == __sv_bah_path => { - spec.bah = ::yaml_serde::from_value(value).unwrap(); - } - _ => unreachable!(), - } + if let Some(upgrades) = status.changes().upgrades.get_mut("v1beta1") { + if let Some(::stackable_versioned::ChangedValue { value, .. }) = upgrades + .extract_first_if(|u| u.json_path == __sv_bah_path) + { + spec.bah = ::yaml_serde::from_value(value) + .expect( + "internal error: {__sv_bah_path} must be serializable for spec.bah", + ); + } + if upgrades.is_empty() { + status.changes().upgrades.remove("v1beta1"); } } spec @@ -99,7 +102,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_bah_path = ::stackable_versioned::jthong_path(parent, "bah"); let __sv_baz_path = ::stackable_versioned::jthong_path(parent, "baz"); let upgrades = status @@ -130,7 +133,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let mut spec = Self { inner_1: __sv_baz.inner_1.into(), inner_2: __sv_baz.inner_2.into(), @@ -150,7 +153,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let mut spec = Self { inner_1: __sv_baz.inner_1.into(), inner_2: __sv_baz.inner_2.into(), @@ -224,7 +227,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_baz_path = ::stackable_versioned::jthong_path(parent, "baz"); let mut spec = Self { bar: __sv_foospec.bah.into(), @@ -244,7 +247,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_baz_path = ::stackable_versioned::jthong_path(parent, "baz"); let mut spec = Self { bah: __sv_foospec.bar.into(), @@ -265,21 +268,24 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_inner_3_path = ::stackable_versioned::jthong_path(parent, "inner_3"); let mut spec = Self { inner_1: __sv_baz.inner_1.into(), inner_2: __sv_baz.inner_2.into(), inner_3: ::std::default::Default::default(), }; - if let Some(upgrades) = status.changes().upgrades.remove(&"v1".to_owned()) { - for ::stackable_versioned::ChangedValue { json_path, value } in upgrades { - match json_path { - json_path if json_path == __sv_inner_3_path => { - spec.inner_3 = ::yaml_serde::from_value(value).unwrap(); - } - _ => unreachable!(), - } + if let Some(upgrades) = status.changes().upgrades.get_mut("v1") { + if let Some(::stackable_versioned::ChangedValue { value, .. }) = upgrades + .extract_first_if(|u| u.json_path == __sv_inner_3_path) + { + spec.inner_3 = ::yaml_serde::from_value(value) + .expect( + "internal error: {__sv_inner_3_path} must be serializable for spec.inner_3", + ); + } + if upgrades.is_empty() { + status.changes().upgrades.remove("v1"); } } spec @@ -297,7 +303,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_inner_3_path = ::stackable_versioned::jthong_path(parent, "inner_3"); let upgrades = status.changes().upgrades.entry("v1".to_owned()).or_default(); upgrades diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_hints.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_hints.rs.snap index b45919ea7..884853255 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_hints.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@conversion_tracking_hints.rs.snap @@ -26,7 +26,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_bar_path = ::stackable_versioned::jthong_path(parent, "bar"); let mut spec = Self { bar: __sv_foo.bar.map(|v| v.tracking_into(status, &__sv_bar_path)), @@ -47,7 +47,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_bar_path = ::stackable_versioned::jthong_path(parent, "bar"); let mut spec = Self { bar: __sv_foo.bar.map(|v| v.tracking_into(status, &__sv_bar_path)), @@ -68,21 +68,23 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_baz_baz_path = ::stackable_versioned::jthong_path(parent, "baz_baz"); let mut spec = Self { bar_bar: __sv_bar.bar_bar.into(), baz_baz: ::std::default::Default::default(), }; - if let Some(upgrades) = status.changes().upgrades.remove(&"v1alpha2".to_owned()) - { - for ::stackable_versioned::ChangedValue { json_path, value } in upgrades { - match json_path { - json_path if json_path == __sv_baz_baz_path => { - spec.baz_baz = ::yaml_serde::from_value(value).unwrap(); - } - _ => unreachable!(), - } + if let Some(upgrades) = status.changes().upgrades.get_mut("v1alpha2") { + if let Some(::stackable_versioned::ChangedValue { value, .. }) = upgrades + .extract_first_if(|u| u.json_path == __sv_baz_baz_path) + { + spec.baz_baz = ::yaml_serde::from_value(value) + .expect( + "internal error: {__sv_baz_baz_path} must be serializable for spec.baz_baz", + ); + } + if upgrades.is_empty() { + status.changes().upgrades.remove("v1alpha2"); } } spec @@ -99,7 +101,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_baz_baz_path = ::stackable_versioned::jthong_path(parent, "baz_baz"); let upgrades = status .changes() 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 fd4d643bb..50a37ba62 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 @@ -42,7 +42,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_baz_path = ::stackable_versioned::jthong_path(parent, "baz"); let mut spec = Self { foo: __sv_foo.foo.into(), @@ -52,14 +52,17 @@ where 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 { - match json_path { - json_path if json_path == __sv_baz_path => { - spec.baz = ::yaml_serde::from_value(value).unwrap(); - } - _ => unreachable!(), - } + if let Some(upgrades) = status.changes().upgrades.get_mut("v1beta1") { + if let Some(::stackable_versioned::ChangedValue { value, .. }) = upgrades + .extract_first_if(|u| u.json_path == __sv_baz_path) + { + spec.baz = ::yaml_serde::from_value(value) + .expect( + "internal error: {__sv_baz_path} must be serializable for spec.baz", + ); + } + if upgrades.is_empty() { + status.changes().upgrades.remove("v1beta1"); } } spec @@ -77,7 +80,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let __sv_baz_path = ::stackable_versioned::jthong_path(parent, "baz"); let upgrades = status .changes() @@ -183,7 +186,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let mut spec = Self { foo: __sv_foo.foo.into(), deprecated_bar: __sv_foo.deprecated_bar.into(), @@ -207,7 +210,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let mut spec = Self { foo: __sv_foo.foo.into(), deprecated_bar: __sv_foo.deprecated_bar.into(), @@ -297,7 +300,7 @@ where status: &mut __SvStatus, parent: &str, ) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let mut spec = Self { foo: __sv_foo.foo.into(), deprecated_bar: __sv_foo.deprecated_bar.into(), @@ -317,7 +320,7 @@ where __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { fn tracking_from(__sv_foo: v1::Foo, status: &mut __SvStatus, parent: &str) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let mut spec = Self { foo: __sv_foo.foo.into(), deprecated_bar: __sv_foo.deprecated_bar.into(), @@ -398,7 +401,7 @@ where __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { fn tracking_from(__sv_foo: v1::Foo, status: &mut __SvStatus, parent: &str) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let mut spec = Self { foo: __sv_foo.foo.into(), deprecated_bar: __sv_foo.deprecated_bar.into(), @@ -417,7 +420,7 @@ where __SvStatus: ::stackable_versioned::TrackingStatus + ::core::default::Default, { fn tracking_from(__sv_foo: v2::Foo, status: &mut __SvStatus, parent: &str) -> Self { - use ::stackable_versioned::TrackingInto as _; + use ::stackable_versioned::{ExtractIfExt as _, TrackingInto as _}; let mut spec = Self { foo: __sv_foo.foo.into(), deprecated_bar: __sv_foo.deprecated_bar.into(), diff --git a/crates/stackable-versioned/CHANGELOG.md b/crates/stackable-versioned/CHANGELOG.md index f8dff1247..7ea0a63bd 100644 --- a/crates/stackable-versioned/CHANGELOG.md +++ b/crates/stackable-versioned/CHANGELOG.md @@ -6,9 +6,15 @@ All notable changes to this project will be documented in this file. ### Added +- Add public `ExtractIfExt` trait ([#13006]). - Re-add support for generics in tracking impls ([#1304]). +### Fixed + +- Correctly handle tracked per-version changes across multiple containers ([#1306]). + [#1304]: https://github.com/stackabletech/operator-rs/pull/1304 +[#1306]: https://github.com/stackabletech/operator-rs/pull/1306 ## [0.13.0] - 2026-10-14 diff --git a/crates/stackable-versioned/src/lib.rs b/crates/stackable-versioned/src/lib.rs index ed9ee1689..708ebb98c 100644 --- a/crates/stackable-versioned/src/lib.rs +++ b/crates/stackable-versioned/src/lib.rs @@ -194,6 +194,36 @@ pub struct UnknownDesiredApiVersionError { pub api_version: String, } +/// This extension trait for (currently only) [`Vec`]s enables the extraction of the first matched +/// element based on a predicate. +pub trait ExtractIfExt { + /// Extracts the first element for which the `predicate` evaluates to `true` and returns it. + fn extract_first_if(&mut self, predicate: impl FnMut(&T) -> bool) -> Option; +} + +impl ExtractIfExt for Vec { + fn extract_first_if(&mut self, predicate: impl FnMut(&T) -> bool) -> Option { + let index = self.iter().position(predicate)?; + Some(self.remove(index)) + } +} + pub fn jthong_path(parent: &str, child: &str) -> String { format!("{parent}.{child}") } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn extract_first_if() { + let mut all = vec![1, 2, 3, 4, 5, 6]; + let first_even = all + .extract_first_if(|i| i % 2 == 0) + .expect("source vec contains 2, 4, 6 which are even"); + + assert_eq!(first_even, 2); + assert_eq!(all.len(), 5); + } +} diff --git a/crates/stackable-versioned/tests/inputs/conversions/pass/persons_to_v1alpha1.json b/crates/stackable-versioned/tests/inputs/conversions/pass/persons_to_v1alpha1.json index 631768ca6..6434b00c2 100644 --- a/crates/stackable-versioned/tests/inputs/conversions/pass/persons_to_v1alpha1.json +++ b/crates/stackable-versioned/tests/inputs/conversions/pass/persons_to_v1alpha1.json @@ -73,7 +73,8 @@ "gender": "Male", "relationship": { "married": { - "with": "Jane Doe" + "with": "Jane Doe", + "since": "1970-01-01" } }, "socials": { diff --git a/crates/stackable-versioned/tests/inputs/conversions/pass/persons_to_v3.json b/crates/stackable-versioned/tests/inputs/conversions/pass/persons_to_v3.json index 4722f0f46..53f46b263 100644 --- a/crates/stackable-versioned/tests/inputs/conversions/pass/persons_to_v3.json +++ b/crates/stackable-versioned/tests/inputs/conversions/pass/persons_to_v3.json @@ -73,7 +73,8 @@ "gender": "Male", "relationship": { "married": { - "with": "Jane Doe" + "with": "Jane Doe", + "since": "1970-01-01" } }, "socials": { diff --git a/crates/stackable-versioned/tests/person.rs b/crates/stackable-versioned/tests/person.rs index a2cf037c6..74fba62a4 100644 --- a/crates/stackable-versioned/tests/person.rs +++ b/crates/stackable-versioned/tests/person.rs @@ -102,17 +102,7 @@ pub mod versioned { pub struct Married { with: String, - // FIXME (@Techassi): Any other version than v3 leads to a panic. There are two issues here: - // - // - The tracking impls for each container assume they are the only container which need to - // access to ChangedValues' `upgrades` and/or `downgrades` fields and currently removes - // the list of `ChangedValue`s for a particular version. The code must only remove the - // entire entry once the list inside is empty. Otherwise, other containers won't be able - // to access the tracked changes. - // - Each tracking impl must only look and remove the changes which matched with the - // expected JSON path. Unmatched JSON paths currently lead to a panic. They should instead - // be ignored. - #[versioned(added(since = "v3"))] + #[versioned(added(since = "v2"))] since: String, } diff --git a/crates/stackable-versioned/tests/snapshots/conversions__pass@persons_to_v1alpha1.json.snap b/crates/stackable-versioned/tests/snapshots/conversions__pass@persons_to_v1alpha1.json.snap index 4ff52d2f4..eab916aad 100644 --- a/crates/stackable-versioned/tests/snapshots/conversions__pass@persons_to_v1alpha1.json.snap +++ b/crates/stackable-versioned/tests/snapshots/conversions__pass@persons_to_v1alpha1.json.snap @@ -146,6 +146,10 @@ input_file: crates/stackable-versioned/tests/inputs/conversions/pass/persons_to_ { "jsonPath": "$.gender", "value": "Male" + }, + { + "jsonPath": "$.relationship.Married.0.since", + "value": "1970-01-01" } ] } @@ -192,9 +196,7 @@ input_file: crates/stackable-versioned/tests/inputs/conversions/pass/persons_to_ { "jsonPath": "$.gender", "value": "Male" - } - ], - "v3": [ + }, { "jsonPath": "$.relationship.Married.0.since", "value": "1970-01-01" diff --git a/crates/stackable-versioned/tests/snapshots/conversions__pass@persons_to_v3.json.snap b/crates/stackable-versioned/tests/snapshots/conversions__pass@persons_to_v3.json.snap index 65c68e008..1db1b46ff 100644 --- a/crates/stackable-versioned/tests/snapshots/conversions__pass@persons_to_v3.json.snap +++ b/crates/stackable-versioned/tests/snapshots/conversions__pass@persons_to_v3.json.snap @@ -110,7 +110,7 @@ input_file: crates/stackable-versioned/tests/inputs/conversions/pass/persons_to_ "lastName": "Doe", "relationship": { "married": { - "since": "", + "since": "1970-01-01", "with": "Jane Doe" } },