fix(versionable)!: wrong derived bounds in the Versionize macro

Over-restrictive derived bounds were in some cases unsatisfiable, making the
`versionize` method uncallable.

BREAKING_CHANGE:
- The `#[versionize(bound = ...)]` attribute is not needed anymore, so it has
been removed.
This commit is contained in:
Nicolas Sarlin
2024-09-18 11:51:25 +02:00
committed by Nicolas Sarlin
parent a631904bd1
commit b63347336b
6 changed files with 165 additions and 253 deletions

View File

@@ -1,11 +1,12 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use syn::{
parse_quote, DeriveInput, ImplGenerics, Item, ItemImpl, Lifetime, Path, Type, WhereClause,
parse_quote, DeriveInput, Generics, ImplGenerics, Item, ItemImpl, Lifetime, Path, Type,
WhereClause,
};
use crate::{
add_lifetime_bound, add_trait_bound, add_trait_where_clause, add_where_lifetime_bound,
add_lifetime_bound, add_trait_where_clause, add_where_lifetime_bound, extend_where_clause,
parse_const_str, DESERIALIZE_TRAIT_NAME, LIFETIME_NAME, SERIALIZE_TRAIT_NAME,
};
@@ -91,6 +92,11 @@ pub(crate) enum AssociatedTypeKind {
/// [`DispatchType`]: crate::dispatch_type::DispatchType
/// [`VersionType`]: crate::dispatch_type::VersionType
pub(crate) trait AssociatedType: Sized {
/// Bounds that will be added on the fields of the ref type definition
const REF_BOUNDS: &'static [&'static str];
/// Bounds that will be added on the fields of the owned type definition
const OWNED_BOUNDS: &'static [&'static str];
/// This will create the alternative of the type that holds a reference to the underlying data
fn new_ref(orig_type: &DeriveInput) -> syn::Result<Self>;
/// This will create the alternative of the type that owns the underlying data
@@ -99,6 +105,30 @@ pub(crate) trait AssociatedType: Sized {
/// Generates the type declaration for this type
fn generate_type_declaration(&self) -> syn::Result<Item>;
/// Returns the kind of associated type, a ref or an owned type
fn kind(&self) -> &AssociatedTypeKind;
/// Returns the generics found in the original type definition
fn orig_type_generics(&self) -> &Generics;
/// Returns the generics and bounds that should be added to the type
fn type_generics(&self) -> syn::Result<Generics> {
let mut generics = self.orig_type_generics().clone();
if let AssociatedTypeKind::Ref(opt_lifetime) = &self.kind() {
if let Some(lifetime) = opt_lifetime {
add_lifetime_bound(&mut generics, lifetime);
}
add_trait_where_clause(&mut generics, self.inner_types()?, Self::REF_BOUNDS)?;
} else {
add_trait_where_clause(&mut generics, self.inner_types()?, Self::OWNED_BOUNDS)?;
}
Ok(generics)
}
/// Generics needed for conversions between the original type and associated types
fn conversion_generics(&self, direction: ConversionDirection) -> syn::Result<Generics>;
/// Generates conversion methods between the origin type and the associated type. If the version
/// type is a ref, the conversion is `From<&'vers OrigType> for Associated<'vers>` because this
/// conversion is used for versioning. If the version type is owned, the conversion is
@@ -109,10 +139,6 @@ pub(crate) trait AssociatedType: Sized {
/// [`Version`]: crate::dispatch_type::VersionType
fn generate_conversion(&self) -> syn::Result<Vec<ItemImpl>>;
/// The lifetime added for this type, if it is a "ref" type. It also returns None if the type is
/// a unit type (no data)
//fn lifetime(&self) -> Option<&Lifetime>;
/// The identifier used to name this type
fn ident(&self) -> Ident;
@@ -144,40 +170,19 @@ pub(crate) struct AssociatingTrait<T> {
owned_type: T,
orig_type: DeriveInput,
trait_path: Path,
/// Bounds that should be added to the generics for the impl
generics_bounds: Vec<String>,
/// Bounds that should be added on the struct attributes
attributes_bounds: Vec<String>,
}
impl<T: AssociatedType> AssociatingTrait<T> {
pub(crate) fn new(
orig_type: &DeriveInput,
name: &str,
generics_bounds: &[&str],
attributes_bounds: &[&str],
) -> syn::Result<Self> {
pub(crate) fn new(orig_type: &DeriveInput, name: &str) -> syn::Result<Self> {
let ref_type = T::new_ref(orig_type)?;
let owned_type = T::new_owned(orig_type)?;
let trait_path = syn::parse_str(name)?;
let generics_bounds = generics_bounds
.iter()
.map(|bound| bound.to_string())
.collect();
let attributes_bounds = attributes_bounds
.iter()
.map(|bound| bound.to_string())
.collect();
Ok(Self {
ref_type,
owned_type,
orig_type: orig_type.clone(),
trait_path,
generics_bounds,
attributes_bounds,
})
}
@@ -189,22 +194,24 @@ impl<T: AssociatedType> AssociatingTrait<T> {
let ref_ident = self.ref_type.ident();
let owned_ident = self.owned_type.ident();
let mut generics = self.orig_type.generics.clone();
for bound in &self.generics_bounds {
add_trait_bound(&mut generics, bound)?;
}
let trait_param = self.ref_type.as_trait_param().transpose()?;
let mut ref_type_generics = generics.clone();
// AssociatedToOrig conversion always has a stricter bound than the other side so we use it
let mut generics = self
.owned_type
.conversion_generics(ConversionDirection::AssociatedToOrig)?;
add_trait_where_clause(
&mut generics,
self.ref_type.inner_types()?,
&self.attributes_bounds,
)?;
// Merge the where clause for the reference type with the one from the owned type
let owned_where_clause = generics.make_where_clause();
if let Some(ref_where_clause) = self
.ref_type
.conversion_generics(ConversionDirection::AssociatedToOrig)?
.where_clause
{
extend_where_clause(owned_where_clause, &ref_where_clause);
}
let mut ref_type_generics = self.ref_type.orig_type_generics().clone();
// If the original type has some generics, we need to add a lifetime bound on them
if let Some(lifetime) = self.ref_type.lifetime() {
add_lifetime_bound(&mut ref_type_generics, lifetime);
@@ -246,8 +253,13 @@ impl<T: AssociatedType> AssociatingTrait<T> {
)
]};
// Creates the type declaration. These types are the output of the versioning process, so
// they should be serializable. Serde might try to add automatic bounds on the type generics
// even if we don't need them, so we use `#[serde(bound = "")]` to disable this. The bounds
// on the generated types should be sufficient.
let owned_tokens = quote! {
#[derive(#serialize_trait, #deserialize_trait)]
#[serde(bound = "")]
#ignored_lints
#owned_decla
@@ -260,6 +272,7 @@ impl<T: AssociatedType> AssociatingTrait<T> {
let ref_tokens = quote! {
#[derive(#serialize_trait)]
#[serde(bound = "")]
#ignored_lints
#ref_decla

View File

@@ -10,10 +10,10 @@ use syn::{
use crate::associated::{
generate_from_trait_impl, generate_try_from_trait_impl, AssociatedType, AssociatedTypeKind,
ConversionDirection,
};
use crate::{
add_lifetime_bound, add_trait_bound, add_trait_where_clause, parse_const_str, LIFETIME_NAME,
UNVERSIONIZE_ERROR_NAME, VERSIONIZE_TRAIT_NAME, VERSION_TRAIT_NAME,
parse_const_str, LIFETIME_NAME, UNVERSIONIZE_ERROR_NAME, UPGRADE_TRAIT_NAME, VERSION_TRAIT_NAME,
};
/// This is the enum that holds all the versions of a specific type. Each variant of the enum is
@@ -47,6 +47,10 @@ fn derive_input_to_enum(input: &DeriveInput) -> syn::Result<ItemEnum> {
}
impl AssociatedType for DispatchType {
const REF_BOUNDS: &'static [&'static str] = &[VERSION_TRAIT_NAME];
const OWNED_BOUNDS: &'static [&'static str] = &[VERSION_TRAIT_NAME];
fn new_ref(orig_type: &DeriveInput) -> syn::Result<Self> {
for lt in orig_type.generics.lifetimes() {
// check for collision with other lifetimes in `orig_type`
@@ -93,7 +97,7 @@ impl AssociatedType for DispatchType {
Ok(ItemEnum {
ident: self.ident(),
generics: self.generics()?,
generics: self.type_generics()?,
attrs: vec![parse_quote! { #[automatically_derived] }],
variants: variants?,
..self.orig_type.clone()
@@ -101,13 +105,41 @@ impl AssociatedType for DispatchType {
.into())
}
fn generate_conversion(&self) -> syn::Result<Vec<ItemImpl>> {
let generics = self.generics()?;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
fn kind(&self) -> &AssociatedTypeKind {
&self.kind
}
fn orig_type_generics(&self) -> &Generics {
&self.orig_type.generics
}
fn conversion_generics(&self, direction: ConversionDirection) -> syn::Result<Generics> {
let mut generics = self.type_generics()?;
let preds = &mut generics.make_where_clause().predicates;
let upgrade_trait: Path = parse_const_str(UPGRADE_TRAIT_NAME);
if let ConversionDirection::AssociatedToOrig = direction {
if let AssociatedTypeKind::Owned = &self.kind {
// Add a bound for each version to be upgradable into the next one
for src_idx in 0..(self.versions_count() - 1) {
let src_ty = self.version_type_at(src_idx)?;
let next_ty = self.version_type_at(src_idx + 1)?;
preds.push(parse_quote! { #src_ty: #upgrade_trait<#next_ty> })
}
}
}
Ok(generics)
}
fn generate_conversion(&self) -> syn::Result<Vec<ItemImpl>> {
match &self.kind {
AssociatedTypeKind::Ref(lifetime) => {
// Wraps the highest version into the dispatch enum
let generics = self.conversion_generics(ConversionDirection::OrigToAssociated)?;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let src_type = self.latest_version_type()?;
let src = parse_quote! { &#lifetime #src_type };
let dest_ident = self.ident();
@@ -126,6 +158,9 @@ impl AssociatedType for DispatchType {
}
AssociatedTypeKind::Owned => {
// Upgrade to the highest version the convert to the main type
let generics = self.conversion_generics(ConversionDirection::AssociatedToOrig)?;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let src_ident = self.ident();
let src = parse_quote! { #src_ident #ty_generics };
let dest_type = self.latest_version_type()?;
@@ -144,6 +179,9 @@ impl AssociatedType for DispatchType {
)?;
// Wraps the highest version into the dispatch enum
let generics = self.conversion_generics(ConversionDirection::OrigToAssociated)?;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let src_type = self.latest_version_type()?;
let src = parse_quote! { #src_type };
let dest_ident = self.ident();
@@ -182,13 +220,13 @@ impl AssociatedType for DispatchType {
}
}
fn as_trait_param(&self) -> Option<syn::Result<&Type>> {
Some(self.latest_version_type())
}
fn inner_types(&self) -> syn::Result<Vec<&Type>> {
self.version_types()
}
fn as_trait_param(&self) -> Option<syn::Result<&Type>> {
Some(self.latest_version_type())
}
}
impl DispatchType {
@@ -200,19 +238,6 @@ impl DispatchType {
)
}
fn generics(&self) -> syn::Result<Generics> {
let mut generics = self.orig_type.generics.clone();
if let AssociatedTypeKind::Ref(Some(lifetime)) = &self.kind {
add_lifetime_bound(&mut generics, lifetime);
}
add_trait_where_clause(&mut generics, self.inner_types()?, &[VERSION_TRAIT_NAME])?;
add_trait_bound(&mut generics, VERSIONIZE_TRAIT_NAME)?;
Ok(generics)
}
/// Returns the number of versions in this dispatch enum
fn versions_count(&self) -> usize {
self.orig_type.variants.len()

View File

@@ -16,12 +16,10 @@ use proc_macro2::Span;
use quote::{quote, ToTokens};
use syn::parse::Parse;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{
parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics, Ident, Lifetime,
LifetimeParam, Path, TraitBound, Type, TypeParam, TypeParamBound,
LifetimeParam, Path, TraitBound, Type, TypeParamBound, WhereClause,
};
use versionize_attribute::VersionizeAttribute;
/// Adds the full path of the current crate name to avoid name clashes in generated code.
macro_rules! crate_full_path {
@@ -39,15 +37,16 @@ pub(crate) const VERSIONIZE_SLICE_TRAIT_NAME: &str = crate_full_path!("Versioniz
pub(crate) const VERSIONIZE_VEC_TRAIT_NAME: &str = crate_full_path!("VersionizeVec");
pub(crate) const UNVERSIONIZE_TRAIT_NAME: &str = crate_full_path!("Unversionize");
pub(crate) const UNVERSIONIZE_VEC_TRAIT_NAME: &str = crate_full_path!("UnversionizeVec");
pub(crate) const UPGRADE_TRAIT_NAME: &str = crate_full_path!("Upgrade");
pub(crate) const UNVERSIONIZE_ERROR_NAME: &str = crate_full_path!("UnversionizeError");
pub(crate) const SERIALIZE_TRAIT_NAME: &str = "::serde::Serialize";
pub(crate) const DESERIALIZE_TRAIT_NAME: &str = "::serde::Deserialize";
pub(crate) const DESERIALIZE_OWNED_TRAIT_NAME: &str = "::serde::de::DeserializeOwned";
use associated::AssociatingTrait;
use crate::version_type::VersionType;
use crate::versionize_attribute::VersionizeAttribute;
/// unwrap a `syn::Result` by extracting the Ok value or returning from the outer function with
/// a compile error
@@ -74,8 +73,6 @@ fn impl_version_trait(input: &DeriveInput) -> proc_macro2::TokenStream {
let version_trait = syn_unwrap!(AssociatingTrait::<VersionType>::new(
input,
VERSION_TRAIT_NAME,
&[SERIALIZE_TRAIT_NAME, DESERIALIZE_OWNED_TRAIT_NAME],
&[VERSIONIZE_TRAIT_NAME, UNVERSIONIZE_TRAIT_NAME]
));
let version_types = syn_unwrap!(version_trait.generate_types_declarations());
@@ -102,16 +99,6 @@ pub fn derive_versions_dispatch(input: TokenStream) -> TokenStream {
let dispatch_trait = syn_unwrap!(AssociatingTrait::<DispatchType>::new(
&input,
DISPATCH_TRAIT_NAME,
&[
VERSIONIZE_TRAIT_NAME,
VERSIONIZE_VEC_TRAIT_NAME,
VERSIONIZE_SLICE_TRAIT_NAME,
UNVERSIONIZE_TRAIT_NAME,
UNVERSIONIZE_VEC_TRAIT_NAME,
SERIALIZE_TRAIT_NAME,
DESERIALIZE_OWNED_TRAIT_NAME
],
&[]
));
let dispatch_types = syn_unwrap!(dispatch_trait.generate_types_declarations());
@@ -137,9 +124,13 @@ pub fn derive_versions_dispatch(input: TokenStream) -> TokenStream {
pub fn derive_versionize(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let attributes = syn_unwrap!(VersionizeAttribute::parse_from_attributes_list(
&input.attrs
));
let attributes = syn_unwrap!(
VersionizeAttribute::parse_from_attributes_list(&input.attrs).and_then(|attr_opt| attr_opt
.ok_or_else(|| syn::Error::new(
Span::call_site(),
"Missing `versionize` attribute for `Versionize`",
)))
);
// If we apply a type conversion before the call to versionize, the type that implements
// the `Version` trait is the target type and not Self
@@ -197,48 +188,9 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
let versionize_slice_trait: Path = parse_const_str(VERSIONIZE_SLICE_TRAIT_NAME);
let unversionize_vec_trait: Path = parse_const_str(UNVERSIONIZE_VEC_TRAIT_NAME);
let mut versionize_generics = trait_generics.clone();
for bound in attributes.versionize_bounds() {
syn_unwrap!(add_type_param_bound(&mut versionize_generics, bound));
}
// Add generic bounds specified by the user with the `bound` attribute
let mut unversionize_generics = trait_generics.clone();
for bound in attributes.unversionize_bounds() {
syn_unwrap!(add_type_param_bound(&mut unversionize_generics, bound));
}
// Add Generics for the `VersionizeVec` and `UnversionizeVec` traits
let mut versionize_slice_generics = versionize_generics.clone();
syn_unwrap!(add_trait_bound(
&mut versionize_slice_generics,
VERSIONIZE_TRAIT_NAME
));
let mut versionize_vec_generics = versionize_generics.clone();
syn_unwrap!(add_trait_bound(
&mut versionize_vec_generics,
VERSIONIZE_OWNED_TRAIT_NAME
));
let mut unversionize_vec_generics = unversionize_generics.clone();
syn_unwrap!(add_trait_bound(
&mut unversionize_vec_generics,
UNVERSIONIZE_TRAIT_NAME
));
// split generics so they can be used inside the generated code
let (_, _, ref_where_clause) = ref_generics.split_for_impl();
let (versionize_impl_generics, _, versionize_where_clause) =
versionize_generics.split_for_impl();
let (unversionize_impl_generics, _, unversionize_where_clause) =
unversionize_generics.split_for_impl();
let (versionize_slice_impl_generics, _, versionize_slice_where_clause) =
versionize_slice_generics.split_for_impl();
let (versionize_vec_impl_generics, _, versionize_vec_where_clause) =
versionize_vec_generics.split_for_impl();
let (unversionize_vec_impl_generics, _, unversionize_vec_where_clause) =
unversionize_vec_generics.split_for_impl();
let (trait_impl_generics, _, trait_where_clause) = trait_generics.split_for_impl();
// If we want to apply a conversion before the call to versionize we need to use the "owned"
// alternative of the dispatch enum to be able to store the conversion result.
@@ -257,8 +209,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
#version_trait_impl
#[automatically_derived]
impl #versionize_impl_generics #versionize_trait for #input_ident #ty_generics
#versionize_where_clause
impl #trait_impl_generics #versionize_trait for #input_ident #ty_generics
#trait_where_clause
{
type Versioned<#lifetime> =
<#dispatch_enum_path #dispatch_generics as
@@ -270,8 +222,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
}
#[automatically_derived]
impl #versionize_impl_generics #versionize_owned_trait for #input_ident #ty_generics
#versionize_where_clause
impl #trait_impl_generics #versionize_owned_trait for #input_ident #ty_generics
#trait_where_clause
{
type VersionedOwned =
<#dispatch_enum_path #dispatch_generics as
@@ -283,8 +235,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
}
#[automatically_derived]
impl #unversionize_impl_generics #unversionize_trait for #input_ident #ty_generics
#unversionize_where_clause
impl #trait_impl_generics #unversionize_trait for #input_ident #ty_generics
#trait_where_clause
{
fn unversionize(#unversionize_arg_name: Self::VersionedOwned) -> Result<Self, #unversionize_error> {
#unversionize_body
@@ -292,8 +244,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
}
#[automatically_derived]
impl #versionize_slice_impl_generics #versionize_slice_trait for #input_ident #ty_generics
#versionize_slice_where_clause
impl #trait_impl_generics #versionize_slice_trait for #input_ident #ty_generics
#trait_where_clause
{
type VersionedSlice<#lifetime> = Vec<<Self as #versionize_trait>::Versioned<#lifetime>> #ref_where_clause;
@@ -302,8 +254,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
}
}
impl #versionize_vec_impl_generics #versionize_vec_trait for #input_ident #ty_generics
#versionize_vec_where_clause
impl #trait_impl_generics #versionize_vec_trait for #input_ident #ty_generics
#trait_where_clause
{
type VersionedVec = Vec<<Self as #versionize_owned_trait>::VersionedOwned> #owned_where_clause;
@@ -314,8 +266,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
}
#[automatically_derived]
impl #unversionize_vec_impl_generics #unversionize_vec_trait for #input_ident #ty_generics
#unversionize_vec_where_clause {
impl #trait_impl_generics #unversionize_vec_trait for #input_ident #ty_generics
#trait_where_clause {
fn unversionize_vec(versioned: Self::VersionedVec) -> Result<Vec<Self>, #unversionize_error> {
versioned
.into_iter()
@@ -425,35 +377,6 @@ fn parse_trait_bound(trait_name: &str) -> syn::Result<TraitBound> {
Ok(parse_quote!(#trait_path))
}
/// Adds a trait bound for `trait_name` on all the generic types in `generics`
fn add_trait_bound(generics: &mut Generics, trait_name: &str) -> syn::Result<()> {
let trait_bound: TraitBound = parse_trait_bound(trait_name)?;
for param in generics.type_params_mut() {
param
.bounds
.push(TypeParamBound::Trait(trait_bound.clone()));
}
Ok(())
}
fn add_type_param_bound(generics: &mut Generics, type_param_bound: &TypeParam) -> syn::Result<()> {
for param in generics.type_params_mut() {
if param.ident == type_param_bound.ident {
param.bounds.extend(type_param_bound.bounds.clone());
return Ok(());
}
}
Err(syn::Error::new(
type_param_bound.span(),
format!(
"Bound type {} not found in target type generics",
type_param_bound.ident
),
))
}
/// Adds a "where clause" bound for all the input types with all the input traits
fn add_trait_where_clause<'a, S: AsRef<str>, I: IntoIterator<Item = &'a Type>>(
generics: &mut Generics,
@@ -475,6 +398,18 @@ fn add_trait_where_clause<'a, S: AsRef<str>, I: IntoIterator<Item = &'a Type>>(
Ok(())
}
/// Extends a where clause with predicates from another one, filtering duplicates
fn extend_where_clause(base_clause: &mut WhereClause, extension_clause: &WhereClause) {
for extend_predicate in &extension_clause.predicates {
if base_clause.predicates.iter().all(|base_predicate| {
base_predicate.to_token_stream().to_string()
!= extend_predicate.to_token_stream().to_string()
}) {
base_clause.predicates.push(extend_predicate.clone());
}
}
}
/// Creates a Result [`syn::punctuated::Punctuated`] from an iterator of Results
fn punctuated_from_iter_result<T, P: Default, I: IntoIterator<Item = syn::Result<T>>>(
iter: I,

View File

@@ -16,9 +16,9 @@ use crate::associated::{
ConversionDirection,
};
use crate::{
add_lifetime_bound, add_trait_where_clause, parse_const_str, parse_trait_bound,
punctuated_from_iter_result, LIFETIME_NAME, UNVERSIONIZE_ERROR_NAME, UNVERSIONIZE_TRAIT_NAME,
VERSIONIZE_OWNED_TRAIT_NAME, VERSIONIZE_TRAIT_NAME,
add_trait_where_clause, parse_const_str, parse_trait_bound, punctuated_from_iter_result,
LIFETIME_NAME, UNVERSIONIZE_ERROR_NAME, UNVERSIONIZE_TRAIT_NAME, VERSIONIZE_OWNED_TRAIT_NAME,
VERSIONIZE_TRAIT_NAME,
};
/// The types generated for a specific version of a given exposed type. These types are identical to
@@ -30,6 +30,9 @@ pub(crate) struct VersionType {
}
impl AssociatedType for VersionType {
const REF_BOUNDS: &'static [&'static str] = &[VERSIONIZE_TRAIT_NAME];
const OWNED_BOUNDS: &'static [&'static str] = &[VERSIONIZE_OWNED_TRAIT_NAME];
fn new_ref(orig_type: &DeriveInput) -> syn::Result<VersionType> {
let lifetime = if is_unit(orig_type) {
None
@@ -173,40 +176,23 @@ impl AssociatedType for VersionType {
}
}
fn as_trait_param(&self) -> Option<syn::Result<&Type>> {
None
}
fn inner_types(&self) -> syn::Result<Vec<&Type>> {
self.orig_type_fields()
.iter()
.map(|field| Ok(&field.ty))
.collect()
}
}
impl VersionType {
/// Returns the fields of the original declaration.
fn orig_type_fields(&self) -> Punctuated<&Field, Comma> {
derive_type_fields(&self.orig_type)
fn as_trait_param(&self) -> Option<syn::Result<&Type>> {
None
}
fn type_generics(&self) -> syn::Result<Generics> {
let mut generics = self.orig_type.generics.clone();
if let AssociatedTypeKind::Ref(opt_lifetime) = &self.kind {
if let Some(lifetime) = opt_lifetime {
add_lifetime_bound(&mut generics, lifetime);
}
add_trait_where_clause(&mut generics, self.inner_types()?, &[VERSIONIZE_TRAIT_NAME])?;
} else {
add_trait_where_clause(
&mut generics,
self.inner_types()?,
&[VERSIONIZE_OWNED_TRAIT_NAME],
)?;
}
fn kind(&self) -> &AssociatedTypeKind {
&self.kind
}
Ok(generics)
fn orig_type_generics(&self) -> &Generics {
&self.orig_type.generics
}
fn conversion_generics(&self, direction: ConversionDirection) -> syn::Result<Generics> {
@@ -224,6 +210,13 @@ impl VersionType {
Ok(generics)
}
}
impl VersionType {
/// Returns the fields of the original declaration.
fn orig_type_fields(&self) -> Punctuated<&Field, Comma> {
derive_type_fields(&self.orig_type)
}
/// Generates the declaration for the Version equivalent of the input struct
fn generate_struct(&self, stru: &DataStruct) -> syn::Result<ItemStruct> {

View File

@@ -2,14 +2,11 @@ use proc_macro2::Span;
use quote::{quote, ToTokens};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{
Attribute, Expr, ExprLit, Ident, Lit, Meta, MetaNameValue, Path, Token, TraitBound, Type,
TypeParam,
};
use syn::{Attribute, Expr, Ident, Lit, Meta, Path, Token, TraitBound, Type};
use crate::{parse_const_str, UNVERSIONIZE_ERROR_NAME, VERSIONIZE_OWNED_TRAIT_NAME};
/// Name of the attribute used to give arguments to our macros
/// Name of the attribute used to give arguments to the `Versionize` macro
const VERSIONIZE_ATTR_NAME: &str = "versionize";
pub(crate) struct VersionizeAttribute {
@@ -17,8 +14,6 @@ pub(crate) struct VersionizeAttribute {
from: Option<Path>,
try_from: Option<Path>,
into: Option<Path>,
versionize_bounds: Vec<TypeParam>,
unversionize_bounds: Vec<TypeParam>,
}
#[derive(Default)]
@@ -27,8 +22,6 @@ struct VersionizeAttributeBuilder {
from: Option<Path>,
try_from: Option<Path>,
into: Option<Path>,
versionize_bounds: Vec<TypeParam>,
unversionize_bounds: Vec<TypeParam>,
}
impl VersionizeAttributeBuilder {
@@ -42,8 +35,6 @@ impl VersionizeAttributeBuilder {
from: self.from,
try_from: self.try_from,
into: self.into,
versionize_bounds: self.versionize_bounds,
unversionize_bounds: self.unversionize_bounds,
})
}
}
@@ -53,18 +44,17 @@ impl VersionizeAttribute {
/// `DispatchType` is the name of the type holding the dispatch enum.
/// Returns an error if no `versionize` attribute has been found, if multiple attributes are
/// present on the same struct or if the attribute is malformed.
pub(crate) fn parse_from_attributes_list(attributes: &[Attribute]) -> syn::Result<Self> {
pub(crate) fn parse_from_attributes_list(
attributes: &[Attribute],
) -> syn::Result<Option<Self>> {
let version_attributes: Vec<&Attribute> = attributes
.iter()
.filter(|attr| attr.path().is_ident(VERSIONIZE_ATTR_NAME))
.collect();
match version_attributes.as_slice() {
[] => Err(syn::Error::new(
Span::call_site(),
"Missing `versionize` attribute for `Versionize`",
)),
[attr] => Self::parse_from_attribute(attr),
[] => Ok(None),
[attr] => Self::parse_from_attribute(attr).map(Some),
[_, attr2, ..] => Err(syn::Error::new(
attr2.span(),
"Multiple `versionize` attributes found",
@@ -91,31 +81,6 @@ impl VersionizeAttribute {
attribute_builder.dispatch_enum = Some(dispatch_enum.clone());
}
}
Meta::List(list) => {
// parse versionize(bound(unversionize = "Type: Bound"))
if list.path.is_ident("bound") {
let name_value: MetaNameValue = list.parse_args()?;
let bound_attr: TypeParam = match &name_value.value {
Expr::Lit(ExprLit {
attrs: _,
lit: Lit::Str(s),
}) => syn::parse_str(&s.value())?,
_ => {
return Err(Self::default_error(meta.span()));
}
};
if name_value.path.is_ident("versionize") {
attribute_builder.versionize_bounds.push(bound_attr);
} else if name_value.path.is_ident("unversionize") {
attribute_builder.unversionize_bounds.push(bound_attr);
} else {
return Err(Self::default_error(meta.span()));
}
} else {
return Err(Self::default_error(meta.span()));
}
}
Meta::NameValue(name_value) => {
// parse versionize(from = "TypeFrom")
if name_value.path.is_ident("from") {
@@ -142,22 +107,11 @@ impl VersionizeAttribute {
Some(parse_path_ignore_quotes(&name_value.value)?);
}
// parse versionize(bound = "Type: Bound")
} else if name_value.path.is_ident("bound") {
let bound_attr: TypeParam = match &name_value.value {
Expr::Lit(ExprLit {
attrs: _,
lit: Lit::Str(s),
}) => syn::parse_str(&s.value())?,
_ => {
return Err(Self::default_error(meta.span()));
}
};
attribute_builder.versionize_bounds.push(bound_attr.clone());
attribute_builder.unversionize_bounds.push(bound_attr);
} else {
return Err(Self::default_error(meta.span()));
}
}
_ => return Err(Self::default_error(meta.span())),
}
}
@@ -213,14 +167,6 @@ impl VersionizeAttribute {
quote! { #arg_name.try_into() }
}
}
pub(crate) fn versionize_bounds(&self) -> &[TypeParam] {
&self.versionize_bounds
}
pub(crate) fn unversionize_bounds(&self) -> &[TypeParam] {
&self.unversionize_bounds
}
}
fn parse_path_ignore_quotes(value: &Expr) -> syn::Result<Path> {

View File

@@ -3,6 +3,6 @@
/// This trait should be implemented for each version of the original type that is not the current
/// one. The upgrade method is called in chains until we get to the last version of the type.
pub trait Upgrade<T> {
type Error: std::error::Error;
type Error: std::error::Error + Send + Sync + 'static;
fn upgrade(self) -> Result<T, Self::Error>;
}