mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-09 22:57:59 -05:00
fix(versionable): compatibility between "convert" and generics
This commit is contained in:
committed by
Nicolas Sarlin
parent
2af4676588
commit
2b14b22820
@@ -36,18 +36,13 @@ fn bigint_to_le_bytes(x: [u64; 6]) -> [u8; 6 * 8] {
|
|||||||
mod g1 {
|
mod g1 {
|
||||||
use tfhe_versionable::Versionize;
|
use tfhe_versionable::Versionize;
|
||||||
|
|
||||||
use crate::backward_compatibility::SerializableG1AffineVersions;
|
|
||||||
use crate::serialization::{InvalidSerializedAffineError, SerializableG1Affine};
|
use crate::serialization::{InvalidSerializedAffineError, SerializableG1Affine};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
||||||
#[serde(try_from = "SerializableG1Affine", into = "SerializableG1Affine")]
|
#[serde(try_from = "SerializableG1Affine", into = "SerializableG1Affine")]
|
||||||
#[versionize(
|
#[versionize(try_from = "SerializableG1Affine", into = "SerializableG1Affine")]
|
||||||
SerializableG1AffineVersions,
|
|
||||||
try_from = "SerializableG1Affine",
|
|
||||||
into = "SerializableG1Affine"
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct G1Affine {
|
pub struct G1Affine {
|
||||||
pub(crate) inner: ark_bls12_381::g1::G1Affine,
|
pub(crate) inner: ark_bls12_381::g1::G1Affine,
|
||||||
@@ -99,11 +94,7 @@ mod g1 {
|
|||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
||||||
#[serde(try_from = "SerializableG1Affine", into = "SerializableG1Affine")]
|
#[serde(try_from = "SerializableG1Affine", into = "SerializableG1Affine")]
|
||||||
#[versionize(
|
#[versionize(try_from = "SerializableG1Affine", into = "SerializableG1Affine")]
|
||||||
SerializableG1AffineVersions,
|
|
||||||
try_from = "SerializableG1Affine",
|
|
||||||
into = "SerializableG1Affine"
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct G1 {
|
pub struct G1 {
|
||||||
pub(crate) inner: ark_bls12_381::G1Projective,
|
pub(crate) inner: ark_bls12_381::G1Projective,
|
||||||
@@ -264,18 +255,13 @@ mod g1 {
|
|||||||
mod g2 {
|
mod g2 {
|
||||||
use tfhe_versionable::Versionize;
|
use tfhe_versionable::Versionize;
|
||||||
|
|
||||||
use crate::backward_compatibility::SerializableG2AffineVersions;
|
|
||||||
use crate::serialization::{InvalidSerializedAffineError, SerializableG2Affine};
|
use crate::serialization::{InvalidSerializedAffineError, SerializableG2Affine};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
||||||
#[serde(try_from = "SerializableG2Affine", into = "SerializableG2Affine")]
|
#[serde(try_from = "SerializableG2Affine", into = "SerializableG2Affine")]
|
||||||
#[versionize(
|
#[versionize(try_from = "SerializableG2Affine", into = "SerializableG2Affine")]
|
||||||
SerializableG2AffineVersions,
|
|
||||||
try_from = "SerializableG2Affine",
|
|
||||||
into = "SerializableG2Affine"
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct G2Affine {
|
pub struct G2Affine {
|
||||||
pub(crate) inner: ark_bls12_381::g2::G2Affine,
|
pub(crate) inner: ark_bls12_381::g2::G2Affine,
|
||||||
@@ -328,11 +314,7 @@ mod g2 {
|
|||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
||||||
#[serde(try_from = "SerializableG2Affine", into = "SerializableG2Affine")]
|
#[serde(try_from = "SerializableG2Affine", into = "SerializableG2Affine")]
|
||||||
#[versionize(
|
#[versionize(try_from = "SerializableG2Affine", into = "SerializableG2Affine")]
|
||||||
SerializableG2AffineVersions,
|
|
||||||
try_from = "SerializableG2Affine",
|
|
||||||
into = "SerializableG2Affine"
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct G2 {
|
pub struct G2 {
|
||||||
pub(crate) inner: ark_bls12_381::G2Projective,
|
pub(crate) inner: ark_bls12_381::G2Projective,
|
||||||
@@ -539,7 +521,6 @@ mod g2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod gt {
|
mod gt {
|
||||||
use crate::backward_compatibility::SerializableFp12Versions;
|
|
||||||
use crate::serialization::InvalidArraySizeError;
|
use crate::serialization::InvalidArraySizeError;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -548,11 +529,7 @@ mod gt {
|
|||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Versionize, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Versionize, Hash)]
|
||||||
#[serde(try_from = "SerializableFp12", into = "SerializableFp12")]
|
#[serde(try_from = "SerializableFp12", into = "SerializableFp12")]
|
||||||
#[versionize(
|
#[versionize(try_from = "SerializableFp12", into = "SerializableFp12")]
|
||||||
SerializableFp12Versions,
|
|
||||||
try_from = "SerializableFp12",
|
|
||||||
into = "SerializableFp12"
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Gt {
|
pub struct Gt {
|
||||||
inner: ark_ec::pairing::PairingOutput<ark_bls12_381::Bls12_381>,
|
inner: ark_ec::pairing::PairingOutput<ark_bls12_381::Bls12_381>,
|
||||||
@@ -697,7 +674,6 @@ mod gt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod zp {
|
mod zp {
|
||||||
use crate::backward_compatibility::SerializableFpVersions;
|
|
||||||
use crate::serialization::InvalidArraySizeError;
|
use crate::serialization::InvalidArraySizeError;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -741,11 +717,7 @@ mod zp {
|
|||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Versionize, Hash, Zeroize)]
|
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Versionize, Hash, Zeroize)]
|
||||||
#[serde(try_from = "SerializableFp", into = "SerializableFp")]
|
#[serde(try_from = "SerializableFp", into = "SerializableFp")]
|
||||||
#[versionize(
|
#[versionize(try_from = "SerializableFp", into = "SerializableFp")]
|
||||||
SerializableFpVersions,
|
|
||||||
try_from = "SerializableFp",
|
|
||||||
into = "SerializableFp"
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Zp {
|
pub struct Zp {
|
||||||
pub(crate) inner: ark_bls12_381::Fr,
|
pub(crate) inner: ark_bls12_381::Fr,
|
||||||
|
|||||||
@@ -36,18 +36,13 @@ fn bigint_to_le_bytes(x: [u64; 7]) -> [u8; 7 * 8] {
|
|||||||
mod g1 {
|
mod g1 {
|
||||||
use tfhe_versionable::Versionize;
|
use tfhe_versionable::Versionize;
|
||||||
|
|
||||||
use crate::backward_compatibility::SerializableG1AffineVersions;
|
|
||||||
use crate::serialization::{InvalidSerializedAffineError, SerializableG1Affine};
|
use crate::serialization::{InvalidSerializedAffineError, SerializableG1Affine};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
||||||
#[serde(try_from = "SerializableG1Affine", into = "SerializableG1Affine")]
|
#[serde(try_from = "SerializableG1Affine", into = "SerializableG1Affine")]
|
||||||
#[versionize(
|
#[versionize(try_from = "SerializableG1Affine", into = "SerializableG1Affine")]
|
||||||
SerializableG1AffineVersions,
|
|
||||||
try_from = "SerializableG1Affine",
|
|
||||||
into = "SerializableG1Affine"
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct G1Affine {
|
pub struct G1Affine {
|
||||||
pub(crate) inner: crate::curve_446::g1::G1Affine,
|
pub(crate) inner: crate::curve_446::g1::G1Affine,
|
||||||
@@ -101,11 +96,7 @@ mod g1 {
|
|||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
||||||
#[serde(try_from = "SerializableG1Affine", into = "SerializableG1Affine")]
|
#[serde(try_from = "SerializableG1Affine", into = "SerializableG1Affine")]
|
||||||
#[versionize(
|
#[versionize(try_from = "SerializableG1Affine", into = "SerializableG1Affine")]
|
||||||
SerializableG1AffineVersions,
|
|
||||||
try_from = "SerializableG1Affine",
|
|
||||||
into = "SerializableG1Affine"
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct G1 {
|
pub struct G1 {
|
||||||
pub(crate) inner: crate::curve_446::g1::G1Projective,
|
pub(crate) inner: crate::curve_446::g1::G1Projective,
|
||||||
@@ -267,7 +258,6 @@ mod g1 {
|
|||||||
mod g2 {
|
mod g2 {
|
||||||
use tfhe_versionable::Versionize;
|
use tfhe_versionable::Versionize;
|
||||||
|
|
||||||
use crate::backward_compatibility::SerializableG2AffineVersions;
|
|
||||||
use crate::serialization::SerializableG2Affine;
|
use crate::serialization::SerializableG2Affine;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -275,11 +265,7 @@ mod g2 {
|
|||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
||||||
#[serde(try_from = "SerializableG2Affine", into = "SerializableG2Affine")]
|
#[serde(try_from = "SerializableG2Affine", into = "SerializableG2Affine")]
|
||||||
#[versionize(
|
#[versionize(try_from = "SerializableG2Affine", into = "SerializableG2Affine")]
|
||||||
SerializableG2AffineVersions,
|
|
||||||
try_from = "SerializableG2Affine",
|
|
||||||
into = "SerializableG2Affine"
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct G2Affine {
|
pub struct G2Affine {
|
||||||
pub(crate) inner: crate::curve_446::g2::G2Affine,
|
pub(crate) inner: crate::curve_446::g2::G2Affine,
|
||||||
@@ -423,11 +409,7 @@ mod g2 {
|
|||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Versionize)]
|
||||||
#[serde(try_from = "SerializableG2Affine", into = "SerializableG2Affine")]
|
#[serde(try_from = "SerializableG2Affine", into = "SerializableG2Affine")]
|
||||||
#[versionize(
|
#[versionize(try_from = "SerializableG2Affine", into = "SerializableG2Affine")]
|
||||||
SerializableG2AffineVersions,
|
|
||||||
try_from = "SerializableG2Affine",
|
|
||||||
into = "SerializableG2Affine"
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct G2 {
|
pub struct G2 {
|
||||||
pub(crate) inner: crate::curve_446::g2::G2Projective,
|
pub(crate) inner: crate::curve_446::g2::G2Projective,
|
||||||
@@ -633,7 +615,6 @@ mod g2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod gt {
|
mod gt {
|
||||||
use crate::backward_compatibility::SerializableFp12Versions;
|
|
||||||
use crate::curve_446::{Fq, Fq12, Fq2};
|
use crate::curve_446::{Fq, Fq12, Fq2};
|
||||||
use crate::serialization::InvalidSerializedAffineError;
|
use crate::serialization::InvalidSerializedAffineError;
|
||||||
|
|
||||||
@@ -812,11 +793,7 @@ mod gt {
|
|||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Versionize, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Versionize, Hash)]
|
||||||
#[serde(try_from = "SerializableFp12", into = "SerializableFp12")]
|
#[serde(try_from = "SerializableFp12", into = "SerializableFp12")]
|
||||||
#[versionize(
|
#[versionize(try_from = "SerializableFp12", into = "SerializableFp12")]
|
||||||
SerializableFp12Versions,
|
|
||||||
try_from = "SerializableFp12",
|
|
||||||
into = "SerializableFp12"
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Gt {
|
pub struct Gt {
|
||||||
pub(crate) inner: ark_ec::pairing::PairingOutput<crate::curve_446::Bls12_446>,
|
pub(crate) inner: ark_ec::pairing::PairingOutput<crate::curve_446::Bls12_446>,
|
||||||
@@ -959,8 +936,6 @@ mod gt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod zp {
|
mod zp {
|
||||||
use crate::backward_compatibility::SerializableFpVersions;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::serialization::InvalidArraySizeError;
|
use crate::serialization::InvalidArraySizeError;
|
||||||
use ark_ff::Fp;
|
use ark_ff::Fp;
|
||||||
@@ -1003,11 +978,7 @@ mod zp {
|
|||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Versionize, Hash, Zeroize)]
|
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Versionize, Hash, Zeroize)]
|
||||||
#[serde(try_from = "SerializableFp", into = "SerializableFp")]
|
#[serde(try_from = "SerializableFp", into = "SerializableFp")]
|
||||||
#[versionize(
|
#[versionize(try_from = "SerializableFp", into = "SerializableFp")]
|
||||||
SerializableFpVersions,
|
|
||||||
try_from = "SerializableFp",
|
|
||||||
into = "SerializableFp"
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Zp {
|
pub struct Zp {
|
||||||
pub inner: crate::curve_446::Fr,
|
pub inner: crate::curve_446::Fr,
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ use syn::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
add_lifetime_bound, add_trait_where_clause, add_where_lifetime_bound, extend_where_clause,
|
add_lifetime_param, add_trait_where_clause, add_where_lifetime_bound_to_generics,
|
||||||
parse_const_str, DESERIALIZE_TRAIT_NAME, LIFETIME_NAME, SERIALIZE_TRAIT_NAME,
|
extend_where_clause, parse_const_str, DESERIALIZE_TRAIT_NAME, LIFETIME_NAME,
|
||||||
|
SERIALIZE_TRAIT_NAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Generates an impl block for the From trait. This will be:
|
/// Generates an impl block for the From trait. This will be:
|
||||||
@@ -116,7 +117,7 @@ pub(crate) trait AssociatedType: Sized {
|
|||||||
let mut generics = self.orig_type_generics().clone();
|
let mut generics = self.orig_type_generics().clone();
|
||||||
if let AssociatedTypeKind::Ref(opt_lifetime) = &self.kind() {
|
if let AssociatedTypeKind::Ref(opt_lifetime) = &self.kind() {
|
||||||
if let Some(lifetime) = opt_lifetime {
|
if let Some(lifetime) = opt_lifetime {
|
||||||
add_lifetime_bound(&mut generics, lifetime);
|
add_lifetime_param(&mut generics, lifetime);
|
||||||
}
|
}
|
||||||
add_trait_where_clause(&mut generics, self.inner_types()?, Self::REF_BOUNDS)?;
|
add_trait_where_clause(&mut generics, self.inner_types()?, Self::REF_BOUNDS)?;
|
||||||
} else {
|
} else {
|
||||||
@@ -214,8 +215,8 @@ impl<T: AssociatedType> AssociatingTrait<T> {
|
|||||||
let mut ref_type_generics = self.ref_type.orig_type_generics().clone();
|
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 the original type has some generics, we need to add a lifetime bound on them
|
||||||
if let Some(lifetime) = self.ref_type.lifetime() {
|
if let Some(lifetime) = self.ref_type.lifetime() {
|
||||||
add_lifetime_bound(&mut ref_type_generics, lifetime);
|
add_lifetime_param(&mut ref_type_generics, lifetime);
|
||||||
add_where_lifetime_bound(&mut ref_type_generics, lifetime);
|
add_where_lifetime_bound_to_generics(&mut ref_type_generics, lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (impl_generics, orig_generics, where_clause) = generics.split_for_impl();
|
let (impl_generics, orig_generics, where_clause) = generics.split_for_impl();
|
||||||
|
|||||||
@@ -42,6 +42,13 @@ pub(crate) const UNVERSIONIZE_ERROR_NAME: &str = crate_full_path!("UnversionizeE
|
|||||||
|
|
||||||
pub(crate) const SERIALIZE_TRAIT_NAME: &str = "::serde::Serialize";
|
pub(crate) const SERIALIZE_TRAIT_NAME: &str = "::serde::Serialize";
|
||||||
pub(crate) const DESERIALIZE_TRAIT_NAME: &str = "::serde::Deserialize";
|
pub(crate) const DESERIALIZE_TRAIT_NAME: &str = "::serde::Deserialize";
|
||||||
|
pub(crate) const FROM_TRAIT_NAME: &str = "::core::convert::From";
|
||||||
|
pub(crate) const TRY_INTO_TRAIT_NAME: &str = "::core::convert::TryInto";
|
||||||
|
pub(crate) const INTO_TRAIT_NAME: &str = "::core::convert::Into";
|
||||||
|
pub(crate) const ERROR_TRAIT_NAME: &str = "::core::error::Error";
|
||||||
|
pub(crate) const SYNC_TRAIT_NAME: &str = "::core::marker::Sync";
|
||||||
|
pub(crate) const SEND_TRAIT_NAME: &str = "::core::marker::Send";
|
||||||
|
pub(crate) const STATIC_LIFETIME_NAME: &str = "'static";
|
||||||
|
|
||||||
use associated::AssociatingTrait;
|
use associated::AssociatingTrait;
|
||||||
|
|
||||||
@@ -140,47 +147,7 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
|||||||
Some(impl_version_trait(&input))
|
Some(impl_version_trait(&input))
|
||||||
};
|
};
|
||||||
|
|
||||||
let dispatch_enum_path = attributes.dispatch_enum();
|
// Parse the name of the traits that we will implement
|
||||||
let dispatch_target = attributes.dispatch_target();
|
|
||||||
let input_ident = &input.ident;
|
|
||||||
let mut ref_generics = input.generics.clone();
|
|
||||||
let mut trait_generics = input.generics.clone();
|
|
||||||
let (_, ty_generics, owned_where_clause) = input.generics.split_for_impl();
|
|
||||||
|
|
||||||
// If the original type has some generics, we need to add bounds on them for
|
|
||||||
// the impl
|
|
||||||
let lifetime = Lifetime::new(LIFETIME_NAME, Span::call_site());
|
|
||||||
add_where_lifetime_bound(&mut ref_generics, &lifetime);
|
|
||||||
|
|
||||||
// The versionize method takes a ref. We need to own the input type in the conversion case
|
|
||||||
// to apply `From<Input> for Target`. This adds a `Clone` bound to have a better error message
|
|
||||||
// if the input type is not Clone.
|
|
||||||
if attributes.needs_conversion() {
|
|
||||||
syn_unwrap!(add_trait_where_clause(
|
|
||||||
&mut trait_generics,
|
|
||||||
[&parse_quote! { Self }],
|
|
||||||
&["Clone"]
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
let dispatch_generics = if attributes.needs_conversion() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(&ty_generics)
|
|
||||||
};
|
|
||||||
|
|
||||||
let dispatch_trait: Path = parse_const_str(DISPATCH_TRAIT_NAME);
|
|
||||||
|
|
||||||
syn_unwrap!(add_trait_where_clause(
|
|
||||||
&mut trait_generics,
|
|
||||||
[&parse_quote!(#dispatch_enum_path #dispatch_generics)],
|
|
||||||
&[format!(
|
|
||||||
"{}<{}>",
|
|
||||||
DISPATCH_TRAIT_NAME,
|
|
||||||
dispatch_target.to_token_stream()
|
|
||||||
)]
|
|
||||||
));
|
|
||||||
|
|
||||||
let versionize_trait: Path = parse_const_str(VERSIONIZE_TRAIT_NAME);
|
let versionize_trait: Path = parse_const_str(VERSIONIZE_TRAIT_NAME);
|
||||||
let versionize_owned_trait: Path = parse_const_str(VERSIONIZE_OWNED_TRAIT_NAME);
|
let versionize_owned_trait: Path = parse_const_str(VERSIONIZE_OWNED_TRAIT_NAME);
|
||||||
let unversionize_trait: Path = parse_const_str(UNVERSIONIZE_TRAIT_NAME);
|
let unversionize_trait: Path = parse_const_str(UNVERSIONIZE_TRAIT_NAME);
|
||||||
@@ -188,19 +155,33 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
|||||||
let versionize_slice_trait: Path = parse_const_str(VERSIONIZE_SLICE_TRAIT_NAME);
|
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 unversionize_vec_trait: Path = parse_const_str(UNVERSIONIZE_VEC_TRAIT_NAME);
|
||||||
|
|
||||||
// split generics so they can be used inside the generated code
|
let input_ident = &input.ident;
|
||||||
let (_, _, ref_where_clause) = ref_generics.split_for_impl();
|
let lifetime = Lifetime::new(LIFETIME_NAME, Span::call_site());
|
||||||
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"
|
// split generics so they can be used inside the generated code
|
||||||
// alternative of the dispatch enum to be able to store the conversion result.
|
let (_, ty_generics, _) = input.generics.split_for_impl();
|
||||||
let versioned_type_kind = if attributes.needs_conversion() {
|
|
||||||
quote! { Owned #owned_where_clause }
|
// Generates the associated types required by the traits
|
||||||
} else {
|
let versioned_type = attributes.versioned_type(&lifetime, &input.generics);
|
||||||
quote! { Ref<#lifetime> #ref_where_clause }
|
let versioned_owned_type = attributes.versioned_owned_type(&input.generics);
|
||||||
};
|
let versioned_type_where_clause =
|
||||||
|
attributes.versioned_type_where_clause(&lifetime, &input.generics);
|
||||||
|
let versioned_owned_type_where_clause =
|
||||||
|
attributes.versioned_owned_type_where_clause(&input.generics);
|
||||||
|
|
||||||
|
// If the original type has some generics, we need to add bounds on them for
|
||||||
|
// the traits impl
|
||||||
|
let versionize_trait_where_clause =
|
||||||
|
syn_unwrap!(attributes.versionize_trait_where_clause(&input.generics));
|
||||||
|
let versionize_owned_trait_where_clause =
|
||||||
|
syn_unwrap!(attributes.versionize_owned_trait_where_clause(&input.generics));
|
||||||
|
let unversionize_trait_where_clause =
|
||||||
|
syn_unwrap!(attributes.unversionize_trait_where_clause(&input.generics));
|
||||||
|
|
||||||
|
let trait_impl_generics = input.generics.split_for_impl().0;
|
||||||
|
|
||||||
let versionize_body = attributes.versionize_method_body();
|
let versionize_body = attributes.versionize_method_body();
|
||||||
|
let versionize_owned_body = attributes.versionize_owned_method_body();
|
||||||
let unversionize_arg_name = Ident::new("versioned", Span::call_site());
|
let unversionize_arg_name = Ident::new("versioned", Span::call_site());
|
||||||
let unversionize_body = attributes.unversionize_method_body(&unversionize_arg_name);
|
let unversionize_body = attributes.unversionize_method_body(&unversionize_arg_name);
|
||||||
let unversionize_error: Path = parse_const_str(UNVERSIONIZE_ERROR_NAME);
|
let unversionize_error: Path = parse_const_str(UNVERSIONIZE_ERROR_NAME);
|
||||||
@@ -210,11 +191,9 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl #trait_impl_generics #versionize_trait for #input_ident #ty_generics
|
impl #trait_impl_generics #versionize_trait for #input_ident #ty_generics
|
||||||
#trait_where_clause
|
#versionize_trait_where_clause
|
||||||
{
|
{
|
||||||
type Versioned<#lifetime> =
|
type Versioned<#lifetime> = #versioned_type #versioned_type_where_clause;
|
||||||
<#dispatch_enum_path #dispatch_generics as
|
|
||||||
#dispatch_trait<#dispatch_target>>::#versioned_type_kind;
|
|
||||||
|
|
||||||
fn versionize(&self) -> Self::Versioned<'_> {
|
fn versionize(&self) -> Self::Versioned<'_> {
|
||||||
#versionize_body
|
#versionize_body
|
||||||
@@ -223,20 +202,18 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl #trait_impl_generics #versionize_owned_trait for #input_ident #ty_generics
|
impl #trait_impl_generics #versionize_owned_trait for #input_ident #ty_generics
|
||||||
#trait_where_clause
|
#versionize_owned_trait_where_clause
|
||||||
{
|
{
|
||||||
type VersionedOwned =
|
type VersionedOwned = #versioned_owned_type #versioned_owned_type_where_clause;
|
||||||
<#dispatch_enum_path #dispatch_generics as
|
|
||||||
#dispatch_trait<#dispatch_target>>::Owned #owned_where_clause;
|
|
||||||
|
|
||||||
fn versionize_owned(self) -> Self::VersionedOwned {
|
fn versionize_owned(self) -> Self::VersionedOwned {
|
||||||
#versionize_body
|
#versionize_owned_body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl #trait_impl_generics #unversionize_trait for #input_ident #ty_generics
|
impl #trait_impl_generics #unversionize_trait for #input_ident #ty_generics
|
||||||
#trait_where_clause
|
#unversionize_trait_where_clause
|
||||||
{
|
{
|
||||||
fn unversionize(#unversionize_arg_name: Self::VersionedOwned) -> Result<Self, #unversionize_error> {
|
fn unversionize(#unversionize_arg_name: Self::VersionedOwned) -> Result<Self, #unversionize_error> {
|
||||||
#unversionize_body
|
#unversionize_body
|
||||||
@@ -245,20 +222,21 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl #trait_impl_generics #versionize_slice_trait for #input_ident #ty_generics
|
impl #trait_impl_generics #versionize_slice_trait for #input_ident #ty_generics
|
||||||
#trait_where_clause
|
#versionize_trait_where_clause
|
||||||
{
|
{
|
||||||
type VersionedSlice<#lifetime> = Vec<<Self as #versionize_trait>::Versioned<#lifetime>> #ref_where_clause;
|
type VersionedSlice<#lifetime> = Vec<<Self as #versionize_trait>::Versioned<#lifetime>> #versioned_type_where_clause;
|
||||||
|
|
||||||
fn versionize_slice(slice: &[Self]) -> Self::VersionedSlice<'_> {
|
fn versionize_slice(slice: &[Self]) -> Self::VersionedSlice<'_> {
|
||||||
slice.iter().map(|val| #versionize_trait::versionize(val)).collect()
|
slice.iter().map(|val| #versionize_trait::versionize(val)).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[automatically_derived]
|
||||||
impl #trait_impl_generics #versionize_vec_trait for #input_ident #ty_generics
|
impl #trait_impl_generics #versionize_vec_trait for #input_ident #ty_generics
|
||||||
#trait_where_clause
|
#versionize_owned_trait_where_clause
|
||||||
{
|
{
|
||||||
|
|
||||||
type VersionedVec = Vec<<Self as #versionize_owned_trait>::VersionedOwned> #owned_where_clause;
|
type VersionedVec = Vec<<Self as #versionize_owned_trait>::VersionedOwned> #versioned_owned_type_where_clause;
|
||||||
|
|
||||||
fn versionize_vec(vec: Vec<Self>) -> Self::VersionedVec {
|
fn versionize_vec(vec: Vec<Self>) -> Self::VersionedVec {
|
||||||
vec.into_iter().map(|val| #versionize_owned_trait::versionize_owned(val)).collect()
|
vec.into_iter().map(|val| #versionize_owned_trait::versionize_owned(val)).collect()
|
||||||
@@ -267,7 +245,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl #trait_impl_generics #unversionize_vec_trait for #input_ident #ty_generics
|
impl #trait_impl_generics #unversionize_vec_trait for #input_ident #ty_generics
|
||||||
#trait_where_clause {
|
#unversionize_trait_where_clause
|
||||||
|
{
|
||||||
fn unversionize_vec(versioned: Self::VersionedVec) -> Result<Vec<Self>, #unversionize_error> {
|
fn unversionize_vec(versioned: Self::VersionedVec) -> Result<Vec<Self>, #unversionize_error> {
|
||||||
versioned
|
versioned
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -335,7 +314,7 @@ pub fn derive_not_versioned(input: TokenStream) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a where clause with a lifetime bound on all the generic types and lifetimes in `generics`
|
/// Adds a where clause with a lifetime bound on all the generic types and lifetimes in `generics`
|
||||||
fn add_where_lifetime_bound(generics: &mut Generics, lifetime: &Lifetime) {
|
fn add_where_lifetime_bound_to_generics(generics: &mut Generics, lifetime: &Lifetime) {
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
for param in generics.params.iter() {
|
for param in generics.params.iter() {
|
||||||
let param_ident = match param {
|
let param_ident = match param {
|
||||||
@@ -359,8 +338,8 @@ fn add_where_lifetime_bound(generics: &mut Generics, lifetime: &Lifetime) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a lifetime bound for all the generic types in `generics`
|
/// Adds a new lifetime param with a bound for all the generic types in `generics`
|
||||||
fn add_lifetime_bound(generics: &mut Generics, lifetime: &Lifetime) {
|
fn add_lifetime_param(generics: &mut Generics, lifetime: &Lifetime) {
|
||||||
generics
|
generics
|
||||||
.params
|
.params
|
||||||
.push(GenericParam::Lifetime(LifetimeParam::new(lifetime.clone())));
|
.push(GenericParam::Lifetime(LifetimeParam::new(lifetime.clone())));
|
||||||
@@ -398,6 +377,27 @@ fn add_trait_where_clause<'a, S: AsRef<str>, I: IntoIterator<Item = &'a Type>>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a "where clause" bound for all the input types with all the input lifetimes
|
||||||
|
fn add_lifetime_where_clause<'a, S: AsRef<str>, I: IntoIterator<Item = &'a Type>>(
|
||||||
|
generics: &mut Generics,
|
||||||
|
types: I,
|
||||||
|
lifetimes: &[S],
|
||||||
|
) -> syn::Result<()> {
|
||||||
|
let preds = &mut generics.make_where_clause().predicates;
|
||||||
|
|
||||||
|
if !lifetimes.is_empty() {
|
||||||
|
let bounds: Vec<Lifetime> = lifetimes
|
||||||
|
.iter()
|
||||||
|
.map(|lifetime| syn::parse_str(lifetime.as_ref()))
|
||||||
|
.collect::<syn::Result<_>>()?;
|
||||||
|
for ty in types {
|
||||||
|
preds.push(parse_quote! { #ty: #(#bounds)+* });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Extends a where clause with predicates from another one, filtering duplicates
|
/// Extends a where clause with predicates from another one, filtering duplicates
|
||||||
fn extend_where_clause(base_clause: &mut WhereClause, extension_clause: &WhereClause) {
|
fn extend_where_clause(base_clause: &mut WhereClause, extension_clause: &WhereClause) {
|
||||||
for extend_predicate in &extension_clause.predicates {
|
for extend_predicate in &extension_clause.predicates {
|
||||||
|
|||||||
@@ -2,47 +2,146 @@ use proc_macro2::Span;
|
|||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::{Attribute, Expr, Ident, Lit, Meta, Path, Token, TraitBound, Type};
|
use syn::{
|
||||||
|
parse_quote, Attribute, Expr, GenericArgument, GenericParam, Generics, Ident, Lifetime, Lit,
|
||||||
|
Meta, Path, PathArguments, Token, TraitBound, Type, TypeParam, WhereClause,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{parse_const_str, UNVERSIONIZE_ERROR_NAME, VERSIONIZE_OWNED_TRAIT_NAME};
|
use crate::{
|
||||||
|
add_lifetime_where_clause, add_trait_where_clause, add_where_lifetime_bound_to_generics,
|
||||||
|
parse_const_str, DISPATCH_TRAIT_NAME, ERROR_TRAIT_NAME, FROM_TRAIT_NAME, INTO_TRAIT_NAME,
|
||||||
|
SEND_TRAIT_NAME, STATIC_LIFETIME_NAME, SYNC_TRAIT_NAME, TRY_INTO_TRAIT_NAME,
|
||||||
|
UNVERSIONIZE_ERROR_NAME, UNVERSIONIZE_TRAIT_NAME, VERSIONIZE_OWNED_TRAIT_NAME,
|
||||||
|
};
|
||||||
|
|
||||||
/// Name of the attribute used to give arguments to the `Versionize` macro
|
/// Name of the attribute used to give arguments to the `Versionize` macro
|
||||||
const VERSIONIZE_ATTR_NAME: &str = "versionize";
|
const VERSIONIZE_ATTR_NAME: &str = "versionize";
|
||||||
|
|
||||||
pub(crate) struct VersionizeAttribute {
|
pub(crate) struct ClassicVersionizeAttribute {
|
||||||
dispatch_enum: Path,
|
dispatch_enum: Path,
|
||||||
from: Option<Path>,
|
}
|
||||||
try_from: Option<Path>,
|
|
||||||
into: Option<Path>,
|
pub(crate) enum ConversionType {
|
||||||
|
Direct,
|
||||||
|
Try,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct ConvertVersionizeAttribute {
|
||||||
|
conversion_target: Path,
|
||||||
|
conversion_type: ConversionType,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) enum VersionizeAttribute {
|
||||||
|
Classic(ClassicVersionizeAttribute),
|
||||||
|
Convert(ConvertVersionizeAttribute),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct VersionizeAttributeBuilder {
|
struct VersionizeAttributeBuilder {
|
||||||
dispatch_enum: Option<Path>,
|
dispatch_enum: Option<Path>,
|
||||||
|
convert: Option<Path>,
|
||||||
|
try_convert: Option<Path>,
|
||||||
from: Option<Path>,
|
from: Option<Path>,
|
||||||
try_from: Option<Path>,
|
try_from: Option<Path>,
|
||||||
into: Option<Path>,
|
into: Option<Path>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VersionizeAttributeBuilder {
|
impl VersionizeAttributeBuilder {
|
||||||
fn build(self) -> Option<VersionizeAttribute> {
|
fn build(self, base_span: &Span) -> syn::Result<VersionizeAttribute> {
|
||||||
// These attributes are mutually exclusive
|
let convert_is_try = self.try_convert.is_some() || self.try_from.is_some();
|
||||||
if self.from.is_some() && self.try_from.is_some() {
|
// User should not use `from` and `try_from` at the same time
|
||||||
return None;
|
let from_target = match (self.from, self.try_from) {
|
||||||
|
(None, None) => None,
|
||||||
|
(Some(_), Some(try_from)) => {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
try_from.span(),
|
||||||
|
"'try_from' and 'from' attributes are mutually exclusive",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
(None, Some(try_from)) => Some(try_from),
|
||||||
|
(Some(from), None) => Some(from),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Same with `convert`/`try_convert`
|
||||||
|
let convert_target = match (self.convert, self.try_convert) {
|
||||||
|
(None, None) => None,
|
||||||
|
(Some(_), Some(try_convert)) => {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
try_convert.span(),
|
||||||
|
"'try_convert' and 'convert' attributes are mutually exclusive",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
(None, Some(try_convert)) => Some(try_convert),
|
||||||
|
(Some(convert), None) => Some(convert),
|
||||||
|
};
|
||||||
|
|
||||||
|
// from/into are here for similarity with serde, but we don't actually support having
|
||||||
|
// different target inside. So we check this to warn the user
|
||||||
|
let from_target =
|
||||||
|
match (from_target, self.into) {
|
||||||
|
(None, None) => None,
|
||||||
|
(None, Some(into)) => return Err(syn::Error::new(
|
||||||
|
into.span(),
|
||||||
|
"unidirectional conversions are not handled, please add a 'from'/'try_from' \
|
||||||
|
attribute or use the 'convert'/'try_convert' attribute instead",
|
||||||
|
)),
|
||||||
|
(Some(from), None) => return Err(syn::Error::new(
|
||||||
|
from.span(),
|
||||||
|
"unidirectional conversions are not handled, please add a 'into' attribute or \
|
||||||
|
use the 'convert'/'try_convert' attribute instead",
|
||||||
|
)),
|
||||||
|
(Some(from), Some(into)) => {
|
||||||
|
if format!("{}", from.to_token_stream())
|
||||||
|
!= format!("{}", into.to_token_stream())
|
||||||
|
{
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
from.span(),
|
||||||
|
"unidirectional conversions are not handled, 'from' and 'into' parameters \
|
||||||
|
should have the same value",
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
Some(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Finally, checks that the user doesn't use both from/into and convert
|
||||||
|
let conversion_target = match (from_target, convert_target) {
|
||||||
|
(None, None) => None,
|
||||||
|
(Some(_), Some(convert)) => {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
convert.span(),
|
||||||
|
"'convert' and 'from'/'into' attributes are mutually exclusive",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
(None, Some(convert)) => Some(convert),
|
||||||
|
(Some(from), None) => Some(from),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(conversion_target) = conversion_target {
|
||||||
|
Ok(VersionizeAttribute::Convert(ConvertVersionizeAttribute {
|
||||||
|
conversion_target,
|
||||||
|
conversion_type: if convert_is_try {
|
||||||
|
ConversionType::Try
|
||||||
|
} else {
|
||||||
|
ConversionType::Direct
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Ok(VersionizeAttribute::Classic(ClassicVersionizeAttribute {
|
||||||
|
dispatch_enum: self.dispatch_enum.ok_or(syn::Error::new(
|
||||||
|
*base_span,
|
||||||
|
"Missing dispatch enum argument",
|
||||||
|
))?,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
Some(VersionizeAttribute {
|
|
||||||
dispatch_enum: self.dispatch_enum?,
|
|
||||||
from: self.from,
|
|
||||||
try_from: self.try_from,
|
|
||||||
into: self.into,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VersionizeAttribute {
|
impl VersionizeAttribute {
|
||||||
/// Find and parse an attribute with the form `#[versionize(DispatchType)]`, where
|
/// Find and parse an attribute with the form `#[versionize(DispatchType)]`, where
|
||||||
/// `DispatchType` is the name of the type holding the dispatch enum.
|
/// `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
|
/// Return an error if no `versionize` attribute has been found, if multiple attributes are
|
||||||
/// present on the same struct or if the attribute is malformed.
|
/// present on the same struct or if the attribute is malformed.
|
||||||
pub(crate) fn parse_from_attributes_list(
|
pub(crate) fn parse_from_attributes_list(
|
||||||
attributes: &[Attribute],
|
attributes: &[Attribute],
|
||||||
@@ -82,8 +181,24 @@ impl VersionizeAttribute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Meta::NameValue(name_value) => {
|
Meta::NameValue(name_value) => {
|
||||||
|
// parse versionize(convert = "TypeConvert")
|
||||||
|
if name_value.path.is_ident("convert") {
|
||||||
|
if attribute_builder.convert.is_some() {
|
||||||
|
return Err(Self::default_error(meta.span()));
|
||||||
|
} else {
|
||||||
|
attribute_builder.convert =
|
||||||
|
Some(parse_path_ignore_quotes(&name_value.value)?);
|
||||||
|
}
|
||||||
|
// parse versionize(try_convert = "TypeTryConvert")
|
||||||
|
} else if name_value.path.is_ident("try_convert") {
|
||||||
|
if attribute_builder.try_convert.is_some() {
|
||||||
|
return Err(Self::default_error(meta.span()));
|
||||||
|
} else {
|
||||||
|
attribute_builder.try_convert =
|
||||||
|
Some(parse_path_ignore_quotes(&name_value.value)?);
|
||||||
|
}
|
||||||
// parse versionize(from = "TypeFrom")
|
// parse versionize(from = "TypeFrom")
|
||||||
if name_value.path.is_ident("from") {
|
} else if name_value.path.is_ident("from") {
|
||||||
if attribute_builder.from.is_some() {
|
if attribute_builder.from.is_some() {
|
||||||
return Err(Self::default_error(meta.span()));
|
return Err(Self::default_error(meta.span()));
|
||||||
} else {
|
} else {
|
||||||
@@ -122,60 +237,289 @@ impl VersionizeAttribute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attribute_builder
|
attribute_builder.build(&attribute.span())
|
||||||
.build()
|
|
||||||
.ok_or_else(|| Self::default_error(attribute.span()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn dispatch_enum(&self) -> &Path {
|
|
||||||
&self.dispatch_enum
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn needs_conversion(&self) -> bool {
|
pub(crate) fn needs_conversion(&self) -> bool {
|
||||||
self.try_from.is_some() || self.from.is_some()
|
match self {
|
||||||
|
VersionizeAttribute::Classic(_) => false,
|
||||||
|
VersionizeAttribute::Convert(_) => true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn dispatch_target(&self) -> Path {
|
/// Return the associated type used in the `Versionize` trait: `MyType::Versioned<'vers>`
|
||||||
self.from
|
///
|
||||||
.as_ref()
|
/// If the type is directly versioned, this will be a type generated by the `VersionDispatch`.
|
||||||
.or(self.try_from.as_ref())
|
///
|
||||||
.map(|target| target.to_owned())
|
/// If we have a conversion before the versioning, we re-use the versioned_owned type of the
|
||||||
.unwrap_or_else(|| {
|
/// conversion target. The versioned_owned is needed because the conversion will create a new
|
||||||
syn::parse_str("Self").expect("Parsing of const value should never fail")
|
/// value, so we can't just use a reference.
|
||||||
})
|
pub(crate) fn versioned_type(
|
||||||
|
&self,
|
||||||
|
lifetime: &Lifetime,
|
||||||
|
input_generics: &Generics,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
match self {
|
||||||
|
VersionizeAttribute::Classic(attr) => {
|
||||||
|
let (_, ty_generics, _) = input_generics.split_for_impl();
|
||||||
|
|
||||||
|
let dispatch_trait: Path = parse_const_str(DISPATCH_TRAIT_NAME);
|
||||||
|
let dispatch_enum_path = &attr.dispatch_enum;
|
||||||
|
quote! {
|
||||||
|
<#dispatch_enum_path #ty_generics as
|
||||||
|
#dispatch_trait<Self>>::Ref<#lifetime>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VersionizeAttribute::Convert(_) => {
|
||||||
|
// 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.
|
||||||
|
self.versioned_owned_type(input_generics)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the where clause for `MyType::Versioned<'vers>`. if `MyType` has generics, this means
|
||||||
|
/// adding a 'vers lifetime bound on them.
|
||||||
|
pub(crate) fn versioned_type_where_clause(
|
||||||
|
&self,
|
||||||
|
lifetime: &Lifetime,
|
||||||
|
input_generics: &Generics,
|
||||||
|
) -> Option<WhereClause> {
|
||||||
|
let mut generics = input_generics.clone();
|
||||||
|
|
||||||
|
add_where_lifetime_bound_to_generics(&mut generics, lifetime);
|
||||||
|
let (_, _, where_clause) = generics.split_for_impl();
|
||||||
|
where_clause.cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the associated type used in the `VersionizeOwned` trait: `MyType::VersionedOwned`
|
||||||
|
///
|
||||||
|
/// If the type is directly versioned, this will be a type generated by the `VersionDispatch`.
|
||||||
|
///
|
||||||
|
/// If we have a conversion before the versioning, we re-use the versioned_owned type of the
|
||||||
|
/// conversion target.
|
||||||
|
pub(crate) fn versioned_owned_type(
|
||||||
|
&self,
|
||||||
|
input_generics: &Generics,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let (_, ty_generics, _) = input_generics.split_for_impl();
|
||||||
|
match self {
|
||||||
|
VersionizeAttribute::Classic(attr) => {
|
||||||
|
let dispatch_trait: Path = parse_const_str(DISPATCH_TRAIT_NAME);
|
||||||
|
let dispatch_enum_path = &attr.dispatch_enum;
|
||||||
|
quote! {
|
||||||
|
<#dispatch_enum_path #ty_generics as
|
||||||
|
#dispatch_trait<Self>>::Owned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VersionizeAttribute::Convert(convert_attr) => {
|
||||||
|
let convert_type_path = &convert_attr.conversion_target;
|
||||||
|
let versionize_owned_trait: Path = parse_const_str(VERSIONIZE_OWNED_TRAIT_NAME);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
<#convert_type_path as #versionize_owned_trait>::VersionedOwned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the where clause for `MyType::VersionedOwned`.
|
||||||
|
///
|
||||||
|
/// This is simply the where clause of the input type.
|
||||||
|
pub(crate) fn versioned_owned_type_where_clause(
|
||||||
|
&self,
|
||||||
|
input_generics: &Generics,
|
||||||
|
) -> Option<WhereClause> {
|
||||||
|
match self {
|
||||||
|
VersionizeAttribute::Classic(_) => input_generics.split_for_impl().2.cloned(),
|
||||||
|
VersionizeAttribute::Convert(convert_attr) => {
|
||||||
|
extract_generics(&convert_attr.conversion_target)
|
||||||
|
.split_for_impl()
|
||||||
|
.2
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the where clause needed to implement the Versionize trait.
|
||||||
|
///
|
||||||
|
/// This is the same as the one for the VersionizeOwned, with an additional "Clone" bound in the
|
||||||
|
/// case where we need to perform a conversion before the versioning.
|
||||||
|
pub(crate) fn versionize_trait_where_clause(
|
||||||
|
&self,
|
||||||
|
input_generics: &Generics,
|
||||||
|
) -> syn::Result<Option<WhereClause>> {
|
||||||
|
// The base bounds for the owned traits are also used for the ref traits
|
||||||
|
let mut generics = input_generics.clone();
|
||||||
|
if self.needs_conversion() {
|
||||||
|
// The versionize method takes a ref. We need to own the input type in the conversion
|
||||||
|
// case to apply `From<Input> for Target`. This adds a `Clone` bound to have
|
||||||
|
// a better error message if the input type is not Clone.
|
||||||
|
add_trait_where_clause(&mut generics, [&parse_quote! { Self }], &["Clone"])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.versionize_owned_trait_where_clause(&generics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the where clause needed to implement the VersionizeOwned trait.
|
||||||
|
///
|
||||||
|
/// If the type is directly versioned, the bound states that the argument points to a valid
|
||||||
|
/// DispatchEnum for this type. This is done by adding a bound on this argument to
|
||||||
|
/// `VersionsDisaptch<Self>`.
|
||||||
|
///
|
||||||
|
/// If there is a conversion, the target of the conversion should implement `VersionizeOwned`
|
||||||
|
/// and `From<Self>`.
|
||||||
|
pub(crate) fn versionize_owned_trait_where_clause(
|
||||||
|
&self,
|
||||||
|
input_generics: &Generics,
|
||||||
|
) -> syn::Result<Option<WhereClause>> {
|
||||||
|
let mut generics = input_generics.clone();
|
||||||
|
match self {
|
||||||
|
VersionizeAttribute::Classic(attr) => {
|
||||||
|
let dispatch_generics = generics.clone();
|
||||||
|
let dispatch_ty_generics = dispatch_generics.split_for_impl().1;
|
||||||
|
let dispatch_enum_path = &attr.dispatch_enum;
|
||||||
|
|
||||||
|
add_trait_where_clause(
|
||||||
|
&mut generics,
|
||||||
|
[&parse_quote!(#dispatch_enum_path #dispatch_ty_generics)],
|
||||||
|
&[format!("{}<Self>", DISPATCH_TRAIT_NAME,)],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
VersionizeAttribute::Convert(convert_attr) => {
|
||||||
|
let convert_type_path = &convert_attr.conversion_target;
|
||||||
|
add_trait_where_clause(
|
||||||
|
&mut generics,
|
||||||
|
[&parse_quote!(#convert_type_path)],
|
||||||
|
&[
|
||||||
|
VERSIONIZE_OWNED_TRAIT_NAME,
|
||||||
|
&format!("{}<Self>", FROM_TRAIT_NAME),
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(generics.split_for_impl().2.cloned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the where clause for the `Unversionize` trait.
|
||||||
|
///
|
||||||
|
/// If the versioning is direct, this is the same bound as the one used for `VersionizeOwned`.
|
||||||
|
///
|
||||||
|
/// If there is a conversion, the target of the conversion need to implement `Unversionize` and
|
||||||
|
/// `Into` or `TryInto<T, E>`, with `E: Error + Send + Sync + 'static`
|
||||||
|
pub(crate) fn unversionize_trait_where_clause(
|
||||||
|
&self,
|
||||||
|
input_generics: &Generics,
|
||||||
|
) -> syn::Result<Option<WhereClause>> {
|
||||||
|
match self {
|
||||||
|
VersionizeAttribute::Classic(_) => {
|
||||||
|
self.versionize_owned_trait_where_clause(input_generics)
|
||||||
|
}
|
||||||
|
VersionizeAttribute::Convert(convert_attr) => {
|
||||||
|
let mut generics = input_generics.clone();
|
||||||
|
let convert_type_path = &convert_attr.conversion_target;
|
||||||
|
let into_trait = match convert_attr.conversion_type {
|
||||||
|
ConversionType::Direct => format!("{}<Self>", INTO_TRAIT_NAME),
|
||||||
|
ConversionType::Try => {
|
||||||
|
// Doing a TryFrom requires that the error
|
||||||
|
// impl Error + Send + Sync + 'static
|
||||||
|
let try_into_trait: Path = parse_const_str(TRY_INTO_TRAIT_NAME);
|
||||||
|
add_trait_where_clause(
|
||||||
|
&mut generics,
|
||||||
|
[&parse_quote!(<#convert_type_path as #try_into_trait<Self>>::Error)],
|
||||||
|
&[ERROR_TRAIT_NAME, SYNC_TRAIT_NAME, SEND_TRAIT_NAME],
|
||||||
|
)?;
|
||||||
|
add_lifetime_where_clause(
|
||||||
|
&mut generics,
|
||||||
|
[&parse_quote!(<#convert_type_path as #try_into_trait<Self>>::Error)],
|
||||||
|
&[STATIC_LIFETIME_NAME],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
format!("{}<Self>", TRY_INTO_TRAIT_NAME)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
add_trait_where_clause(
|
||||||
|
&mut generics,
|
||||||
|
[&parse_quote!(#convert_type_path)],
|
||||||
|
&[
|
||||||
|
UNVERSIONIZE_TRAIT_NAME,
|
||||||
|
&format!("{}<Self>", FROM_TRAIT_NAME),
|
||||||
|
&into_trait,
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(generics.split_for_impl().2.cloned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the body of the versionize method.
|
||||||
pub(crate) fn versionize_method_body(&self) -> proc_macro2::TokenStream {
|
pub(crate) fn versionize_method_body(&self) -> proc_macro2::TokenStream {
|
||||||
let versionize_owned_trait: TraitBound = parse_const_str(VERSIONIZE_OWNED_TRAIT_NAME);
|
let versionize_owned_trait: TraitBound = parse_const_str(VERSIONIZE_OWNED_TRAIT_NAME);
|
||||||
self.into
|
|
||||||
.as_ref()
|
match self {
|
||||||
.map(|target| {
|
VersionizeAttribute::Classic(_) => {
|
||||||
quote! {
|
|
||||||
#versionize_owned_trait::versionize_owned(#target::from(self.to_owned()))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
quote! {
|
quote! {
|
||||||
self.into()
|
self.into()
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
VersionizeAttribute::Convert(convert_attr) => {
|
||||||
|
let convert_type_path = with_turbofish(&convert_attr.conversion_target);
|
||||||
|
quote! {
|
||||||
|
#versionize_owned_trait::versionize_owned(#convert_type_path::from(self.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the body of the versionize_owned method.
|
||||||
|
pub(crate) fn versionize_owned_method_body(&self) -> proc_macro2::TokenStream {
|
||||||
|
let versionize_owned_trait: TraitBound = parse_const_str(VERSIONIZE_OWNED_TRAIT_NAME);
|
||||||
|
|
||||||
|
match self {
|
||||||
|
VersionizeAttribute::Classic(_) => {
|
||||||
|
quote! {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VersionizeAttribute::Convert(convert_attr) => {
|
||||||
|
let convert_type_path = with_turbofish(&convert_attr.conversion_target);
|
||||||
|
quote! {
|
||||||
|
#versionize_owned_trait::versionize_owned(#convert_type_path::from(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the body of the unversionize method.
|
||||||
pub(crate) fn unversionize_method_body(&self, arg_name: &Ident) -> proc_macro2::TokenStream {
|
pub(crate) fn unversionize_method_body(&self, arg_name: &Ident) -> proc_macro2::TokenStream {
|
||||||
let error: Type = parse_const_str(UNVERSIONIZE_ERROR_NAME);
|
let error: Type = parse_const_str(UNVERSIONIZE_ERROR_NAME);
|
||||||
if let Some(target) = &self.from {
|
match self {
|
||||||
quote! { #target::unversionize(#arg_name).map(|value| value.into()) }
|
VersionizeAttribute::Classic(_) => {
|
||||||
} else if let Some(target) = &self.try_from {
|
quote! { #arg_name.try_into() }
|
||||||
let target_name = format!("{}", target.to_token_stream());
|
}
|
||||||
quote! { #target::unversionize(#arg_name).and_then(|value| TryInto::<Self>::try_into(value)
|
VersionizeAttribute::Convert(convert_attr) => {
|
||||||
.map_err(|e| #error::conversion(#target_name, e)))
|
let target = with_turbofish(&convert_attr.conversion_target);
|
||||||
|
match convert_attr.conversion_type {
|
||||||
|
ConversionType::Direct => {
|
||||||
|
quote! { #target::unversionize(#arg_name).map(|value| value.into()) }
|
||||||
|
}
|
||||||
|
ConversionType::Try => {
|
||||||
|
let target_name = format!("{}", target.to_token_stream());
|
||||||
|
quote! { #target::unversionize(#arg_name).and_then(|value| TryInto::<Self>::try_into(value)
|
||||||
|
.map_err(|e| #error::conversion(#target_name, e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
quote! { #arg_name.try_into() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allow the user to give type arguments as `#[versionize(MyType)]` as well as
|
||||||
|
/// `#[versionize("MyType")]`
|
||||||
fn parse_path_ignore_quotes(value: &Expr) -> syn::Result<Path> {
|
fn parse_path_ignore_quotes(value: &Expr) -> syn::Result<Path> {
|
||||||
match &value {
|
match &value {
|
||||||
Expr::Path(expr_path) => Ok(expr_path.path.clone()),
|
Expr::Path(expr_path) => Ok(expr_path.path.clone()),
|
||||||
@@ -192,3 +536,37 @@ fn parse_path_ignore_quotes(value: &Expr) -> syn::Result<Path> {
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the same type but with generics that use the turbofish syntax. Converts
|
||||||
|
/// `MyStruct<T>` into `MyStruct::<T>`
|
||||||
|
fn with_turbofish(path: &Path) -> Path {
|
||||||
|
let mut with_turbo = path.clone();
|
||||||
|
|
||||||
|
for segment in with_turbo.segments.iter_mut() {
|
||||||
|
if let PathArguments::AngleBracketed(generics) = &mut segment.arguments {
|
||||||
|
generics.colon2_token = Some(Token));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with_turbo
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract the generics inside a type
|
||||||
|
fn extract_generics(path: &Path) -> Generics {
|
||||||
|
let mut generics = Generics::default();
|
||||||
|
|
||||||
|
if let Some(last_segment) = path.segments.last() {
|
||||||
|
if let PathArguments::AngleBracketed(args) = &last_segment.arguments {
|
||||||
|
for arg in &args.args {
|
||||||
|
if let GenericArgument::Type(Type::Path(type_path)) = arg {
|
||||||
|
if let Some(ident) = type_path.path.get_ident() {
|
||||||
|
let param = TypeParam::from(ident.clone());
|
||||||
|
generics.params.push(GenericParam::Type(param));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generics
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,48 +3,57 @@
|
|||||||
use tfhe_versionable::{Unversionize, Versionize, VersionsDispatch};
|
use tfhe_versionable::{Unversionize, Versionize, VersionsDispatch};
|
||||||
|
|
||||||
#[derive(Clone, Versionize)]
|
#[derive(Clone, Versionize)]
|
||||||
#[versionize(SerializableMyStructVersions, from = SerializableMyStruct, into = SerializableMyStruct)]
|
// To mimic serde parameters, this can also be expressed as
|
||||||
struct MyStruct {
|
// "#[versionize(from = SerializableMyStruct, into = SerializableMyStruct)]"
|
||||||
|
#[versionize(convert = "SerializableMyStruct<T>")]
|
||||||
|
struct MyStruct<T> {
|
||||||
val: u64,
|
val: u64,
|
||||||
|
generics: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Versionize)]
|
#[derive(Versionize)]
|
||||||
#[versionize(SerializableMyStructVersions)]
|
#[versionize(SerializableMyStructVersions)]
|
||||||
struct SerializableMyStruct {
|
struct SerializableMyStruct<T> {
|
||||||
high: u32,
|
high: u32,
|
||||||
low: u32,
|
low: u32,
|
||||||
|
generics: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(VersionsDispatch)]
|
#[derive(VersionsDispatch)]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
enum SerializableMyStructVersions {
|
enum SerializableMyStructVersions<T> {
|
||||||
V0(SerializableMyStruct),
|
V0(SerializableMyStruct<T>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MyStruct> for SerializableMyStruct {
|
impl<T> From<MyStruct<T>> for SerializableMyStruct<T> {
|
||||||
fn from(value: MyStruct) -> Self {
|
fn from(value: MyStruct<T>) -> Self {
|
||||||
println!("{}", value.val);
|
|
||||||
Self {
|
Self {
|
||||||
high: (value.val >> 32) as u32,
|
high: (value.val >> 32) as u32,
|
||||||
low: (value.val & 0xffffffff) as u32,
|
low: (value.val & 0xffffffff) as u32,
|
||||||
|
generics: value.generics,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SerializableMyStruct> for MyStruct {
|
impl<T> From<SerializableMyStruct<T>> for MyStruct<T> {
|
||||||
fn from(value: SerializableMyStruct) -> Self {
|
fn from(value: SerializableMyStruct<T>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
val: ((value.high as u64) << 32) | (value.low as u64),
|
val: ((value.high as u64) << 32) | (value.low as u64),
|
||||||
|
generics: value.generics,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let stru = MyStruct { val: 37 };
|
let stru = MyStruct {
|
||||||
|
val: 37,
|
||||||
|
generics: 90,
|
||||||
|
};
|
||||||
|
|
||||||
let serialized = bincode::serialize(&stru.versionize()).unwrap();
|
let serialized = bincode::serialize(&stru.versionize()).unwrap();
|
||||||
|
|
||||||
let stru_decoded = MyStruct::unversionize(bincode::deserialize(&serialized).unwrap()).unwrap();
|
let stru_decoded: MyStruct<i32> =
|
||||||
|
MyStruct::unversionize(bincode::deserialize(&serialized).unwrap()).unwrap();
|
||||||
|
|
||||||
assert_eq!(stru.val, stru_decoded.val)
|
assert_eq!(stru.val, stru_decoded.val)
|
||||||
}
|
}
|
||||||
|
|||||||
51
utils/tfhe-versionable/tests/convert_with_bounds.rs
Normal file
51
utils/tfhe-versionable/tests/convert_with_bounds.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
//! Checks compatibility between the "convert" feature and bounds on the From/Into trait
|
||||||
|
|
||||||
|
use tfhe_versionable::{Unversionize, Versionize, VersionsDispatch};
|
||||||
|
|
||||||
|
#[derive(Clone, Versionize)]
|
||||||
|
#[versionize(try_convert = "SerializableMyStruct")]
|
||||||
|
struct MyStruct<T> {
|
||||||
|
generics: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Versionize)]
|
||||||
|
#[versionize(SerializableMyStructVersions)]
|
||||||
|
struct SerializableMyStruct {
|
||||||
|
concrete: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(VersionsDispatch)]
|
||||||
|
#[allow(unused)]
|
||||||
|
enum SerializableMyStructVersions {
|
||||||
|
V0(SerializableMyStruct),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<u64>> From<MyStruct<T>> for SerializableMyStruct {
|
||||||
|
fn from(value: MyStruct<T>) -> Self {
|
||||||
|
Self {
|
||||||
|
concrete: value.generics.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: TryFrom<u64>> TryFrom<SerializableMyStruct> for MyStruct<T> {
|
||||||
|
fn try_from(value: SerializableMyStruct) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
generics: value.concrete.try_into()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Error = T::Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let stru = MyStruct { generics: 90u32 };
|
||||||
|
|
||||||
|
let serialized = bincode::serialize(&stru.versionize()).unwrap();
|
||||||
|
|
||||||
|
let stru_decoded: MyStruct<u32> =
|
||||||
|
MyStruct::unversionize(bincode::deserialize(&serialized).unwrap()).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(stru.generics, stru_decoded.generics)
|
||||||
|
}
|
||||||
58
utils/tfhe-versionable/tests/convert_with_generics.rs
Normal file
58
utils/tfhe-versionable/tests/convert_with_generics.rs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
//! Checks compatibility between the "convert" feature and generics
|
||||||
|
|
||||||
|
use tfhe_versionable::{Unversionize, Versionize, VersionsDispatch};
|
||||||
|
|
||||||
|
#[derive(Clone, Versionize)]
|
||||||
|
#[versionize(convert = "SerializableMyStruct<T>")]
|
||||||
|
struct MyStruct<T> {
|
||||||
|
val: u64,
|
||||||
|
generics: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Versionize)]
|
||||||
|
#[versionize(SerializableMyStructVersions)]
|
||||||
|
struct SerializableMyStruct<T> {
|
||||||
|
high: u32,
|
||||||
|
low: u32,
|
||||||
|
generics: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(VersionsDispatch)]
|
||||||
|
#[allow(unused)]
|
||||||
|
enum SerializableMyStructVersions<T> {
|
||||||
|
V0(SerializableMyStruct<T>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<MyStruct<T>> for SerializableMyStruct<T> {
|
||||||
|
fn from(value: MyStruct<T>) -> Self {
|
||||||
|
Self {
|
||||||
|
high: (value.val >> 32) as u32,
|
||||||
|
low: (value.val & 0xffffffff) as u32,
|
||||||
|
generics: value.generics,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<SerializableMyStruct<T>> for MyStruct<T> {
|
||||||
|
fn from(value: SerializableMyStruct<T>) -> Self {
|
||||||
|
Self {
|
||||||
|
val: ((value.high as u64) << 32) | (value.low as u64),
|
||||||
|
generics: value.generics,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let stru = MyStruct {
|
||||||
|
val: 37,
|
||||||
|
generics: 90,
|
||||||
|
};
|
||||||
|
|
||||||
|
let serialized = bincode::serialize(&stru.versionize()).unwrap();
|
||||||
|
|
||||||
|
let stru_decoded: MyStruct<i32> =
|
||||||
|
MyStruct::unversionize(bincode::deserialize(&serialized).unwrap()).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(stru.val, stru_decoded.val)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user