Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#[versioned(version(name = "v1alpha1"), version(name = "v1"), version(name = "v2"))]
// ---
struct Foo {
#[versioned(
// This tests two additional things:
// - that both unquoted and quoted usage works
// - that the renamed name does get picked up correctly by the conversion function
changed(since = "v1", from_type = "u16", from_name = "bar", convert_with = u16_to_u32),
changed(since = "v2", from_type = "u32", convert_with = "u32_to_u64")
)]
baz: u64,
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 24 additions & 7 deletions crates/stackable-versioned-macros/src/attrs/item/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,19 @@ impl CommonItemAttributes {
);
}
}

// The convert_with argument only makes sense to use when the
// type changed
if let Some(convert_func) = change.convert_with.as_ref() {
if change.from_type.is_none() {
errors.push(
Error::custom(
"the `convert_with` argument must be used in combination with `from_type`",
)
.with_span(&convert_func.span()),
);
}
}
}

errors.finish()
Expand Down Expand Up @@ -307,9 +320,10 @@ impl CommonItemAttributes {
actions.insert(
*change.since,
ItemStatus::Change {
convert_with: change.convert_with.as_deref().cloned(),
from_ident: from_ident.clone(),
to_ident: ident,
from_type: from_ty.clone(),
to_ident: ident,
to_type: ty,
},
);
Expand Down Expand Up @@ -356,9 +370,10 @@ impl CommonItemAttributes {
actions.insert(
*change.since,
ItemStatus::Change {
convert_with: change.convert_with.as_deref().cloned(),
from_ident: from_ident.clone(),
to_ident: ident,
from_type: from_ty.clone(),
to_ident: ident,
to_type: ty,
},
);
Expand Down Expand Up @@ -431,12 +446,14 @@ fn default_default_fn() -> SpannedValue<Path> {
///
/// Example usage:
/// - `changed(since = "...", from_name = "...")`
/// - `changed(since = "...", from_name = "..." from_type="...")`
/// - `changed(since = "...", from_name = "...", from_type="...")`
/// - `changed(since = "...", from_name = "...", from_type="...", convert_with = "...")`
#[derive(Clone, Debug, FromMeta)]
pub(crate) struct ChangedAttributes {
pub(crate) since: SpannedValue<Version>,
pub(crate) from_name: Option<SpannedValue<String>>,
pub(crate) from_type: Option<SpannedValue<Type>>,
pub struct ChangedAttributes {
pub since: SpannedValue<Version>,
pub from_name: Option<SpannedValue<String>>,
pub from_type: Option<SpannedValue<Type>>,
pub convert_with: Option<SpannedValue<Path>>,
}

/// For the deprecated() action
Expand Down
19 changes: 15 additions & 4 deletions crates/stackable-versioned-macros/src/codegen/item/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,18 +167,29 @@ impl VersionedField {
_,
ItemStatus::Change {
from_ident: old_field_ident,
convert_with,
to_ident,
..
},
) => {
quote! {
) => match convert_with {
// The user specified a custom conversion function which
// will be used here instead of the default .into() call
// which utilizes From impls.
Some(convert_fn) => quote! {
#to_ident: #convert_fn(#from_struct_ident.#old_field_ident),
},
// Default .into() call using From impls.
None => quote! {
#to_ident: #from_struct_ident.#old_field_ident.into(),
}
}
},
},
(old, next) => {
let next_field_ident = next.get_ident();
let old_field_ident = old.get_ident();

// NOTE (@Techassi): Do we really need .into() here. I'm
// currently not sure why it is there and if it is needed
// in some edge cases.
quote! {
#next_field_ident: #from_struct_ident.#old_field_ident.into(),
}
Expand Down
3 changes: 2 additions & 1 deletion crates/stackable-versioned-macros/src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl From<&ModuleAttributes> for Vec<VersionDefinition> {
}

#[derive(Debug, PartialEq)]
pub(crate) enum ItemStatus {
pub enum ItemStatus {
Addition {
ident: IdentString,
default_fn: Path,
Expand All @@ -81,6 +81,7 @@ pub(crate) enum ItemStatus {
ty: Type,
},
Change {
convert_with: Option<Path>,
from_ident: IdentString,
to_ident: IdentString,
from_type: Type,
Expand Down
59 changes: 59 additions & 0 deletions crates/stackable-versioned-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,10 @@ mod utils;
/// - `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.
/// - `convert_with` to provide a custom conversion function instead of using
/// a [`From`] implementation by default. 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 not fail.
///
/// ```
/// # use stackable_versioned_macros::versioned;
Expand Down Expand Up @@ -521,6 +525,61 @@ mod utils;
/// This automatic generation can be skipped to enable a custom implementation
/// for more complex conversions.
///
/// ### Custom conversion function at field level
///
/// As stated in the [`changed()`](#changed-action) section, a custom conversion
/// function can be provided using the `convert_with` argument. A simple example
/// looks like this:
///
/// ```
/// # use stackable_versioned_macros::versioned;
/// #[versioned(
/// version(name = "v1alpha1"),
/// version(name = "v1beta1")
/// )]
/// pub struct Foo {
/// #[versioned(changed(
/// since = "v1beta1",
/// from_type = "u8",
/// convert_with = "u8_to_u16"
/// ))]
/// bar: u16,
/// }
///
/// fn u8_to_u16(old: u8) -> u16 {
/// old as u16
/// }
/// ```
///
/// <details>
/// <summary>Expand Generated Code</summary>
///
/// ```
/// pub mod v1alpha1 {
/// use super::*;
/// pub struct Foo {
/// pub bar: u8,
/// }
/// }
///
/// impl ::std::convert::From<v1alpha1::Foo> for v1beta1::Foo {
/// fn from(__sv_foo: v1alpha1::Foo) -> Self {
/// Self {
/// bar: u8_to_u16(__sv_foo.bar),
/// }
/// }
/// }
///
/// pub mod v1beta1 {
/// use super::*;
/// pub struct Foo {
/// pub bar: u16,
/// }
/// }
/// ```
///
/// </details>
///
/// ### Skipping at the Container Level
///
/// Disabling this behavior at the container level results in no `From`
Expand Down
Loading