From af6d8a7b62adda4118f51f28b265f4e47bfa42e9 Mon Sep 17 00:00:00 2001 From: Techassi Date: Tue, 29 Sep 2026 09:30:02 +0200 Subject: [PATCH 1/2] feat(versioned): Add "map" hint This now allows users to hint for Hash and BTreeMaps by converting the values and passing the keys unchanged. --- crates/stackable-versioned-macros/src/attrs/item/field.rs | 1 + crates/stackable-versioned-macros/src/codegen/item/field.rs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/crates/stackable-versioned-macros/src/attrs/item/field.rs b/crates/stackable-versioned-macros/src/attrs/item/field.rs index 47811b5b9..8c45c4471 100644 --- a/crates/stackable-versioned-macros/src/attrs/item/field.rs +++ b/crates/stackable-versioned-macros/src/attrs/item/field.rs @@ -92,4 +92,5 @@ impl FieldAttributes { pub enum Hint { Option, Vec, + Map, } diff --git a/crates/stackable-versioned-macros/src/codegen/item/field.rs b/crates/stackable-versioned-macros/src/codegen/item/field.rs index 96774a78d..ea074aeea 100644 --- a/crates/stackable-versioned-macros/src/codegen/item/field.rs +++ b/crates/stackable-versioned-macros/src/codegen/item/field.rs @@ -442,6 +442,9 @@ impl VersionedField { Hint::Vec => { quote! { into_iter().map(|v| v.tracking_into(status, &#json_path_ident)).collect() } } + Hint::Map => { + quote! { into_iter().map(|(k, v)| (k, v.tracking_into(status, &#json_path_ident))).collect() } + } } } else { quote! { tracking_into(status, &#json_path_ident) } @@ -454,6 +457,7 @@ impl VersionedField { match hint { Hint::Option => quote! { map(Into::into) }, Hint::Vec => quote! { into_iter().map(Into::into).collect() }, + Hint::Map => quote! { into_iter().map(|(k, v)| (k, v.into())).collect() }, } } else { quote! { into() } From 4fb3ce724ef0ebcba21faa2c986c0b4eba90df56 Mon Sep 17 00:00:00 2001 From: Techassi Date: Tue, 29 Sep 2026 09:52:24 +0200 Subject: [PATCH 2/2] test(versioned): Add snapshot test for "map" hint --- .../tests/inputs/pass/hint_map.rs | 31 ++++++++++++++++ ...d_macros__snapshots__pass@hint_map.rs.snap | 35 +++++++++++++++++++ .../tests/trybuild.rs | 1 + 3 files changed, 67 insertions(+) create mode 100644 crates/stackable-versioned-macros/tests/inputs/pass/hint_map.rs create mode 100644 crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@hint_map.rs.snap diff --git a/crates/stackable-versioned-macros/tests/inputs/pass/hint_map.rs b/crates/stackable-versioned-macros/tests/inputs/pass/hint_map.rs new file mode 100644 index 000000000..9e9784615 --- /dev/null +++ b/crates/stackable-versioned-macros/tests/inputs/pass/hint_map.rs @@ -0,0 +1,31 @@ +use std::collections::HashMap; + +use stackable_versioned::versioned; +// --- +#[versioned(version(name = "v1alpha1"), version(name = "v1alpha2"))] +// --- +pub(crate) mod versioned { + pub(crate) struct Foo { + #[versioned( + changed(since = "v1alpha2", from_type = "HashMap"), + hint(map) + )] + bar: HashMap, + } +} +// --- +struct Bar(u8); + +impl From for Bar { + fn from(value: u8) -> Self { + Self(value) + } +} + +impl From for u8 { + fn from(value: Bar) -> Self { + value.0 + } +} + +fn main() {} diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@hint_map.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@hint_map.rs.snap new file mode 100644 index 000000000..0a4eb3c23 --- /dev/null +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshots__pass@hint_map.rs.snap @@ -0,0 +1,35 @@ +--- +source: crates/stackable-versioned-macros/src/lib.rs +expression: formatted +input_file: crates/stackable-versioned-macros/tests/inputs/pass/hint_map.rs +--- +#[automatically_derived] +pub(crate) mod v1alpha1 { + use super::*; + pub struct Foo { + pub bar: HashMap, + } +} +#[automatically_derived] +impl ::core::convert::From for v1alpha2::Foo { + fn from(__sv_foo: v1alpha1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into_iter().map(|(k, v)| (k, v.into())).collect(), + } + } +} +#[automatically_derived] +impl ::core::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1alpha2::Foo) -> Self { + Self { + bar: __sv_foo.bar.into_iter().map(|(k, v)| (k, v.into())).collect(), + } + } +} +#[automatically_derived] +pub(crate) mod v1alpha2 { + use super::*; + pub struct Foo { + pub bar: HashMap, + } +} diff --git a/crates/stackable-versioned-macros/tests/trybuild.rs b/crates/stackable-versioned-macros/tests/trybuild.rs index 90ec9b20a..94eb6dd48 100644 --- a/crates/stackable-versioned-macros/tests/trybuild.rs +++ b/crates/stackable-versioned-macros/tests/trybuild.rs @@ -28,6 +28,7 @@ mod inputs { // mod docs; // mod downgrade_with; // mod enum_fields; + // mod hint_map; // mod module; // mod module_preserve; // mod renamed_field;