diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 608b771..797c121 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -17,8 +17,6 @@ categories = ["encoding"] proc-macro = true [dependencies] -darling = "0.20.3" -proc-macro-error = "1.0.4" proc-macro2 = "1.0.66" quote = "1.0.32" syn = { version = "2.0.28", features = ["full", "extra-traits", "parsing"] } diff --git a/derive/src/ast/attributes.rs b/derive/src/ast/attributes.rs new file mode 100644 index 0000000..168a413 --- /dev/null +++ b/derive/src/ast/attributes.rs @@ -0,0 +1,437 @@ +use std::{collections::HashMap, fmt::Display, str::FromStr}; + +use proc_macro2::Span; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + token, Attribute, Error, LitInt, LitStr, Token, +}; + +mod kw { + syn::custom_keyword!(start); + syn::custom_keyword!(end); + syn::custom_keyword!(convert_fn); + syn::custom_keyword!(default_fn); + syn::custom_keyword!(fields_name); + syn::custom_keyword!(revision); + syn::custom_keyword!(variant_index); + syn::custom_keyword!(order); + syn::custom_keyword!(discriminant); +} + +#[derive(Debug)] +pub struct GroupOption { + key: K, + _paren: token::Paren, + value: Punctuated, +} + +impl Parse for GroupOption +where + K: Parse, + V: Parse, +{ + fn parse(input: ParseStream) -> syn::Result { + let content; + Ok(Self { + key: input.parse()?, + _paren: parenthesized!(content in input), + value: content.parse_terminated(|x| x.parse(), Token![,])?, + }) + } +} + +#[derive(Debug)] +pub struct ValueOption { + key: K, + _eq: token::Eq, + value: V, +} + +impl Parse for ValueOption +where + K: Parse, + V: Parse, +{ + fn parse(input: ParseStream) -> syn::Result { + Ok(Self { + key: input.parse()?, + _eq: input.parse()?, + value: input.parse()?, + }) + } +} + +#[derive(Debug)] +pub struct SpannedLit { + pub value: V, + pub span: Span, +} + +impl Parse for SpannedLit +where + V: FromStr, + V::Err: Display, +{ + fn parse(input: ParseStream) -> syn::Result { + let lit_int: LitInt = input.parse()?; + let span = lit_int.span(); + let value = lit_int.base10_parse()?; + Ok(Self { + span, + value, + }) + } +} + +pub trait AttributeOptions: Sized { + type Option: Parse; + + fn finish(path: Span, options: Vec) -> syn::Result; +} + +/// Used for parsing attribute options directly instead of being wrapped in `#[revision(..)]` +pub struct Direct(pub O); + +impl Parse for Direct +where + O: AttributeOptions, +{ + fn parse(input: ParseStream) -> syn::Result { + let span = input.span(); + let options = input.parse_terminated(|input| O::Option::parse(input), Token![,])?; + let options = options.into_iter().collect::>(); + O::finish(span, options).map(Direct) + } +} + +#[derive(Debug)] +pub struct FilteredAttributes { + pub options: O, + pub other: Vec, +} + +impl Parse for FilteredAttributes { + fn parse(input: ParseStream) -> syn::Result { + let attrs = input.call(Attribute::parse_outer)?; + let mut other = Vec::new(); + let mut options = Vec::new(); + for attr in attrs { + if !attr.path().is_ident("revision") { + other.push(attr); + continue; + } + + let parsed_options = + attr.parse_args_with(Punctuated::::parse_terminated)?; + options.extend(parsed_options.into_iter()) + } + + let options = O::finish(Span::call_site(), options)?; + + Ok(Self { + options, + other, + }) + } +} + +#[derive(Default, Debug)] +pub struct FieldOptions { + pub start: Option>, + pub end: Option>, + pub convert: Option, + pub default: Option, +} + +impl FieldOptions { + pub fn exists_at(&self, revision: usize) -> bool { + self.start.as_ref().map(|x| x.value).unwrap_or(0) <= revision + && self.end.as_ref().map(|x| x.value).unwrap_or(usize::MAX) > revision + } +} + +pub enum FieldOption { + Start(ValueOption>), + End(ValueOption>), + Convert(ValueOption), + Default(ValueOption), +} + +impl Parse for FieldOption { + fn parse(input: ParseStream) -> syn::Result { + if input.peek(kw::start) { + return Ok(FieldOption::Start(input.parse()?)); + } + if input.peek(kw::end) { + return Ok(FieldOption::End(input.parse()?)); + } + if input.peek(kw::convert_fn) { + return Ok(FieldOption::Convert(input.parse()?)); + } + if input.peek(kw::default_fn) { + return Ok(FieldOption::Default(input.parse()?)); + } + + Err(input.error("invalid field option")) + } +} + +impl AttributeOptions for FieldOptions { + type Option = FieldOption; + + fn finish(_span: Span, options: Vec) -> syn::Result { + let mut res = FieldOptions::default(); + + let mut end_kw = None; + + for option in options { + match option { + FieldOption::Start(x) => { + if res.start.is_some() { + return Err(Error::new(x.key.span(), "tried to set an option twice")); + } + res.start = Some(x.value); + } + FieldOption::End(x) => { + if res.end.is_some() { + return Err(Error::new(x.key.span(), "tried to set an option twice")); + } + end_kw = Some(x.key); + res.end = Some(x.value); + } + FieldOption::Convert(x) => { + if res.convert.is_some() { + return Err(Error::new(x.key.span(), "tried to set an option twice")); + } + res.convert = Some(x.value); + } + FieldOption::Default(x) => { + if res.default.is_some() { + return Err(Error::new(x.key.span(), "tried to set an option twice")); + } + res.default = Some(x.value); + } + } + } + + if let Some(kw) = end_kw { + if res.convert.is_none() { + return Err(Error::new( + kw.span(), + "setting a ending revision for a field also requires a convert_fn", + )); + } + } + + Ok(res) + } +} + +#[derive(Debug)] +pub struct ItemOptions { + pub revision: Option, +} + +pub enum ItemOption { + Revision(ValueOption), +} + +impl Parse for ItemOption { + fn parse(input: ParseStream) -> syn::Result { + if input.peek(kw::revision) { + return Ok(ItemOption::Revision(input.parse()?)); + } + + return Err(input.error("invalid item option")); + } +} + +impl AttributeOptions for ItemOptions { + type Option = ItemOption; + + fn finish(_path: Span, options: Vec) -> syn::Result { + let mut revision = None; + + for option in options { + match option { + ItemOption::Revision(x) => { + if revision.is_some() { + return Err(Error::new(x.key.span(), "tried to set an option twice")); + } + + revision = Some(x.value.base10_parse()?); + } + } + } + + Ok(Self { + revision, + }) + } +} + +#[derive(Default, Debug)] +pub struct VariantOptions { + pub start: Option>, + pub end: Option>, + pub convert: Option, + pub default: Option, + pub fields_name: Option, + pub overrides: HashMap, +} + +#[derive(Default, Debug)] +pub struct VariantOverrides { + pub revision: Option>, + pub discriminant: Option>, +} + +impl VariantOptions { + pub fn exists_at(&self, revision: usize) -> bool { + self.start.as_ref().map(|x| x.value).unwrap_or(0) <= revision + && self.end.as_ref().map(|x| x.value).unwrap_or(usize::MAX) > revision + } +} + +pub enum VariantOption { + Start(ValueOption>), + End(ValueOption>), + Convert(ValueOption), + Default(ValueOption), + Fields(ValueOption), + Override(GroupOption), +} + +pub enum VariantOverride { + Discriminant(ValueOption>), + Revision(ValueOption>), +} + +impl Parse for VariantOption { + fn parse(input: ParseStream) -> syn::Result { + if input.peek(kw::start) { + return Ok(VariantOption::Start(input.parse()?)); + } + if input.peek(kw::end) { + return Ok(VariantOption::End(input.parse()?)); + } + if input.peek(kw::convert_fn) { + return Ok(VariantOption::Convert(input.parse()?)); + } + if input.peek(kw::default_fn) { + return Ok(VariantOption::Default(input.parse()?)); + } + if input.peek(kw::fields_name) { + return Ok(VariantOption::Fields(input.parse()?)); + } + if input.peek(Token![override]) { + return Ok(VariantOption::Override(input.parse()?)); + } + + Err(input.error("invalid field option")) + } +} + +impl Parse for VariantOverride { + fn parse(input: ParseStream) -> syn::Result { + if input.peek(kw::discriminant) { + return Ok(VariantOverride::Discriminant(input.parse()?)); + } + if input.peek(kw::revision) { + return Ok(VariantOverride::Revision(input.parse()?)); + } + Err(input.error("invalid field override")) + } +} + +impl AttributeOptions for VariantOptions { + type Option = VariantOption; + fn finish(_span: Span, options: Vec) -> syn::Result { + let mut res = VariantOptions::default(); + + let mut end_kw = None; + + for option in options { + match option { + VariantOption::Start(x) => { + if res.start.is_some() { + return Err(Error::new(x.key.span(), "tried to set an option twice")); + } + res.start = Some(x.value); + } + VariantOption::End(x) => { + if res.end.is_some() { + return Err(Error::new(x.key.span(), "tried to set an option twice")); + } + end_kw = Some(x.key); + res.end = Some(x.value); + } + VariantOption::Convert(x) => { + if res.convert.is_some() { + return Err(Error::new(x.key.span(), "tried to set an option twice")); + } + res.convert = Some(x.value); + } + VariantOption::Default(x) => { + if res.default.is_some() { + return Err(Error::new(x.key.span(), "tried to set an option twice")); + } + res.default = Some(x.value); + } + VariantOption::Fields(x) => { + if res.fields_name.is_some() { + return Err(Error::new(x.key.span(), "tried to set an option twice")); + } + res.fields_name = Some(x.value); + } + VariantOption::Override(x) => { + let mut overrides = VariantOverrides::default(); + for x in x.value.into_iter() { + match x { + VariantOverride::Discriminant(x) => { + if overrides.discriminant.is_some() { + return Err(Error::new( + x.key.span(), + "tried to set an override option twice", + )); + } + overrides.discriminant = Some(x.value); + } + VariantOverride::Revision(x) => { + if overrides.revision.is_some() { + return Err(Error::new( + x.key.span(), + "tried to set an override option twice", + )); + } + overrides.revision = Some(x.value); + } + } + } + let Some(revision) = overrides.revision.as_ref() else { + return Err(Error::new( + x.key.span(), + "missing the revision on which the override applies", + )); + }; + let revision = revision.value; + res.overrides.insert(revision, overrides); + } + } + } + + if let Some(kw) = end_kw { + if res.convert.is_none() { + return Err(Error::new( + kw.span(), + "setting a ending revision for a variant also requires a convert_fn", + )); + } + } + + Ok(res) + } +} diff --git a/derive/src/ast/mod.rs b/derive/src/ast/mod.rs new file mode 100644 index 0000000..e154330 --- /dev/null +++ b/derive/src/ast/mod.rs @@ -0,0 +1,265 @@ +use quote::{format_ident, ToTokens}; +use syn::{ + braced, parenthesized, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + token::{self, Brace, Paren}, + Expr, Generics, Ident, Index, Result, Token, Type, Visibility, +}; + +mod attributes; +mod visit; +pub use attributes::{Direct, FieldOptions, FilteredAttributes, ItemOptions, VariantOptions}; +pub use visit::*; + +#[derive(Debug)] +pub struct Item { + pub attrs: FilteredAttributes, + pub vis: Visibility, + pub kind: ItemKind, +} + +impl Parse for Item { + fn parse(input: ParseStream) -> Result { + Ok(Self { + attrs: input.parse()?, + vis: input.parse()?, + kind: input.parse()?, + }) + } +} + +#[derive(Debug)] +pub enum ItemKind { + Enum(Enum), + Struct(Struct), +} + +impl Parse for ItemKind { + fn parse(input: ParseStream) -> Result { + if input.peek(Token![enum]) { + return Ok(ItemKind::Enum(input.parse()?)); + } + + if input.peek(Token![struct]) { + return Ok(ItemKind::Struct(input.parse()?)); + } + + Err(input.error("unsupported item, revision only supporst structs and enums.")) + } +} + +#[derive(Debug)] +pub struct Enum { + pub enum_: Token![enum], + pub name: Ident, + pub generics: Generics, + pub braces: Brace, + pub variants: Punctuated, +} + +impl Parse for Enum { + fn parse(input: ParseStream) -> Result { + let content; + Ok(Enum { + enum_: input.parse()?, + name: input.parse()?, + generics: input.parse()?, + braces: braced!(content in input), + variants: content.parse_terminated(Variant::parse, Token![,])?, + }) + } +} + +#[derive(Debug)] +pub struct Variant { + pub attrs: FilteredAttributes, + pub ident: Ident, + pub fields: Fields, + pub discriminant: Option<(Token![=], Expr)>, +} + +impl Variant { + /// Returns the name of the fields struct of this variant + pub fn fields_name(&self, enum_name: &str) -> Ident { + self.attrs + .options + .fields_name + .as_ref() + .map(|x| Ident::new(&x.value(), x.span())) + .unwrap_or_else(|| { + Ident::new(&format!("{}{}Fields", enum_name, self.ident), self.ident.span()) + }) + } +} + +impl Parse for Variant { + fn parse(input: ParseStream) -> Result { + let attrs = input.parse()?; + let ident = input.parse()?; + let fields = if input.peek(token::Paren) { + let content; + let paren = parenthesized!(content in input); + let mut fields = content.parse_terminated(Field::parse_unnamed, Token![,])?; + fields.iter_mut().enumerate().for_each(|(idx, f)| { + f.name = FieldName::Index(Index::from(idx)); + }); + Fields::Unnamed { + paren, + fields, + } + } else if input.peek(token::Brace) { + let content; + let brace = braced!(content in input); + let fields = content.parse_terminated(Field::parse_named, Token![,])?; + Fields::Named { + brace, + fields, + } + } else { + Fields::Unit + }; + + let discriminant = if input.peek(Token![:]) { + Some((input.parse()?, input.parse()?)) + } else { + None + }; + + Ok(Self { + attrs, + ident, + fields, + discriminant, + }) + } +} + +#[derive(Debug)] +pub struct Struct { + pub struct_: Token![struct], + pub name: Ident, + pub generics: Generics, + pub fields: Fields, +} + +impl Parse for Struct { + fn parse(input: ParseStream) -> Result { + let struct_ = input.parse()?; + let name = input.parse()?; + let generics = input.parse()?; + let fields = if input.peek(token::Paren) { + let content; + let paren = parenthesized!(content in input); + let mut fields = content.parse_terminated(Field::parse_unnamed, Token![,])?; + fields.iter_mut().enumerate().for_each(|(idx, f)| { + f.name = FieldName::Index(Index::from(idx)); + }); + input.parse::()?; + Fields::Unnamed { + paren, + fields, + } + } else if input.peek(token::Brace) { + let content; + let brace = braced!(content in input); + let fields = content.parse_terminated(Field::parse_named, Token![,])?; + Fields::Named { + brace, + fields, + } + } else { + input.parse::()?; + Fields::Unit + }; + + Ok(Self { + struct_, + name, + generics, + fields, + }) + } +} + +#[derive(Debug)] +pub enum Fields { + Named { + brace: Brace, + fields: Punctuated, + }, + Unnamed { + paren: Paren, + fields: Punctuated, + }, + Unit, +} + +#[derive(Debug)] +pub enum FieldName { + Ident(Ident), + Index(Index), +} + +impl FieldName { + pub fn to_binding(&self) -> Ident { + match self { + FieldName::Ident(x) => x.clone(), + FieldName::Index(x) => { + format_ident!("field_{}", x.index) + } + } + } +} + +impl ToTokens for FieldName { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + match self { + FieldName::Ident(x) => x.to_tokens(tokens), + FieldName::Index(x) => x.to_tokens(tokens), + } + } +} + +#[derive(Debug)] +pub struct Field { + pub attrs: FilteredAttributes, + pub vis: Visibility, + pub name: FieldName, + pub colon_token: Option, + pub ty: Type, +} + +impl Field { + pub fn parse_unnamed(input: ParseStream) -> syn::Result { + let attrs = input.parse()?; + let vis = input.parse()?; + // This is later fixed + let name = FieldName::Index(Index::from(0)); + let ty = input.parse()?; + + Ok(Self { + attrs, + vis, + name, + colon_token: None, + ty, + }) + } + + pub fn parse_named(input: ParseStream) -> syn::Result { + let attrs = input.parse()?; + let vis = input.parse()?; + let name = FieldName::Ident(input.parse()?); + let colon_token = Some(input.parse()?); + let ty = input.parse()?; + + Ok(Self { + attrs, + vis, + name, + colon_token, + ty, + }) + } +} diff --git a/derive/src/ast/visit.rs b/derive/src/ast/visit.rs new file mode 100644 index 0000000..09a8064 --- /dev/null +++ b/derive/src/ast/visit.rs @@ -0,0 +1,96 @@ +use super::{Enum, Field, Fields, Item, ItemKind, Struct, Variant}; + +pub trait Visit<'ast>: Sized { + fn visit_item(&mut self, i: &'ast Item) -> syn::Result<()> { + visit_item(self, i) + } + + fn visit_item_kind(&mut self, i: &'ast ItemKind) -> syn::Result<()> { + visit_item_kind(self, i) + } + + fn visit_enum(&mut self, i: &'ast Enum) -> syn::Result<()> { + visit_enum(self, i) + } + + fn visit_struct(&mut self, i: &'ast Struct) -> syn::Result<()> { + visit_struct(self, i) + } + + fn visit_variant(&mut self, i: &'ast Variant) -> syn::Result<()> { + visit_variant(self, i) + } + + fn visit_fields(&mut self, i: &'ast Fields) -> syn::Result<()> { + visit_fields(self, i) + } + + fn visit_field(&mut self, i: &'ast Field) -> syn::Result<()> { + let _ = i; + Ok(()) + } +} + +pub fn visit_item<'ast, T>(this: &mut T, item: &'ast Item) -> syn::Result<()> +where + T: Visit<'ast>, +{ + this.visit_item_kind(&item.kind) +} + +pub fn visit_item_kind<'ast, T>(this: &mut T, kind: &'ast ItemKind) -> syn::Result<()> +where + T: Visit<'ast>, +{ + match kind { + ItemKind::Enum(e) => this.visit_enum(e)?, + ItemKind::Struct(s) => this.visit_struct(s)?, + } + Ok(()) +} + +pub fn visit_enum<'ast, T>(this: &mut T, e: &'ast Enum) -> syn::Result<()> +where + T: Visit<'ast>, +{ + for variant in e.variants.iter() { + this.visit_variant(variant)? + } + Ok(()) +} + +pub fn visit_struct<'ast, T>(this: &mut T, s: &'ast Struct) -> syn::Result<()> +where + T: Visit<'ast>, +{ + this.visit_fields(&s.fields) +} + +pub fn visit_variant<'ast, T>(this: &mut T, s: &'ast Variant) -> syn::Result<()> +where + T: Visit<'ast>, +{ + this.visit_fields(&s.fields) +} + +pub fn visit_fields<'ast, T>(this: &mut T, f: &'ast Fields) -> syn::Result<()> +where + T: Visit<'ast>, +{ + match f { + Fields::Named { + fields, + .. + } + | Fields::Unnamed { + fields, + .. + } => { + for f in fields { + this.visit_field(f)? + } + Ok(()) + } + Fields::Unit => Ok(()), + } +} diff --git a/derive/src/common.rs b/derive/src/common.rs deleted file mode 100644 index b36b552..0000000 --- a/derive/src/common.rs +++ /dev/null @@ -1,80 +0,0 @@ -/// Describes a structure and it's fields. -#[derive(Debug)] -pub(crate) struct GenericDescriptor { - pub ident: syn::Ident, - pub vis: syn::Visibility, - pub attrs: Vec, - pub generics: syn::Generics, - pub revision: u16, - pub fields: Vec, - pub kind: Kind, -} - -/// Describes a structure and it's fields. -#[derive(Debug)] -pub(crate) enum Kind { - Unit, - Tuple, - Struct, - Enum, -} - -/// An interface for generating serialzer and deserializer -/// implementations for a Rust data type. -pub trait Descriptor { - /// Returns the serializer code block as a token stream. - fn generate_serializer(&self) -> proc_macro2::TokenStream; - /// Returns the deserializer code block as a token stream. - fn generate_deserializer(&self) -> proc_macro2::TokenStream; - /// Returns the curent revision. - fn revision(&self) -> u16; - - fn reexpand(&self) -> proc_macro2::TokenStream; -} - -/// A trait that enables checking whether a certain field -/// exists at a specified revision. -pub(crate) trait Exists { - // Get the start revision for this field - fn start_revision(&self) -> u16; - // Get the end revision for this field - fn end_revision(&self) -> Option; - // Get any sub revision for this field - fn sub_revision(&self) -> u16; - // Check if this field exists for this revision - fn exists_at(&self, revision: u16) -> bool { - // All fields have an initial start revision - revision >= self.start_revision() - // Not all fields have an end revision specified - && self.end_revision().map(|x| revision < x).unwrap_or(true) - } -} - -#[cfg(test)] -mod tests { - use super::Exists; - - #[test] - fn test_exists_at() { - impl Exists for u32 { - fn start_revision(&self) -> u16 { - 3 - } - - fn end_revision(&self) -> Option { - Some(5) - } - - fn sub_revision(&self) -> u16 { - 0 - } - } - - let test = 1234; - assert!(!test.exists_at(2)); - assert!(test.exists_at(3)); - assert!(test.exists_at(4)); - assert!(!test.exists_at(5)); - assert!(!test.exists_at(6)); - } -} diff --git a/derive/src/descriptors/enum_desc.rs b/derive/src/descriptors/enum_desc.rs deleted file mode 100644 index e1bd926..0000000 --- a/derive/src/descriptors/enum_desc.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::common::{Descriptor, Exists, GenericDescriptor, Kind}; -use crate::fields::enum_inner::*; -use crate::fields::enum_struct::*; -use crate::fields::enum_tuple::*; -use crate::helpers::compute_revision; -use quote::quote; - -pub(crate) type EnumDescriptor = GenericDescriptor; - -impl EnumDescriptor { - pub fn new(input: &syn::ItemEnum) -> Self { - // Create the new descriptor - let mut descriptor = EnumDescriptor { - ident: input.ident.clone(), - vis: input.vis.clone(), - generics: input.generics.clone(), - attrs: input.attrs.clone(), - revision: 1, - fields: vec![], - kind: Kind::Enum, - }; - // Parse the enum variants - descriptor.parse_enum_variants(&input.variants); - // Compute the enum revision - descriptor.revision = compute_revision(&descriptor.fields); - // Check field attributes - for field in &descriptor.fields { - field.check_attributes(descriptor.revision); - } - // Return the descriptor - descriptor - } - - fn parse_enum_variants( - &mut self, - variants: &syn::punctuated::Punctuated, - ) { - for (i, variant) in variants.iter().enumerate() { - match variant.fields { - syn::Fields::Unnamed(_) => self.fields.push(EnumInner::EnumTuple(EnumTuple::new( - self.revision, - variant, - i as u32, - ))), - syn::Fields::Named(_) => self.fields.push(EnumInner::EnumStruct(EnumStruct::new( - self.revision, - variant, - i as u32, - ))), - syn::Fields::Unit => self.fields.push(EnumInner::EnumTuple(EnumTuple::new( - self.revision, - variant, - i as u32, - ))), - } - } - } -} - -impl Descriptor for EnumDescriptor { - // Generate the serializer for this type - fn generate_serializer(&self) -> proc_macro2::TokenStream { - // Get the current revision - let revision = self.revision; - // Create a new token stream - let mut serializer = proc_macro2::TokenStream::new(); - // Extend the token stream for each field - for field in self.fields.iter().filter(|x| x.exists_at(revision)) { - serializer.extend(field.generate_serializer(self.revision)); - } - // Output the token stream - quote! { - revision::Revisioned::serialize_revisioned(&#revision, writer)?; - match self { - #serializer - } - Ok(()) - } - } - // Generate the deserializer for this type - fn generate_deserializer(&self) -> proc_macro2::TokenStream { - // Create a new token stream - let mut deserializer = proc_macro2::TokenStream::new(); - // Extend the token stream for each revision - for i in 1..=self.revision { - // Create a new token stream for the struct revision `i`. - let mut variant = proc_macro2::TokenStream::new(); - // Generate field and semantic deserializers for all fields. - for field in &self.fields { - variant.extend(field.generate_deserializer(self.revision, i)); - } - - let name = &self.ident; - // Generate the deserializer match arm for revision `i`. - deserializer.extend(quote! { - #i => match variant { - #variant - v => return Err(revision::Error::Deserialize({ - let res = format!( - concat!("Unknown '", stringify!(#name) ,"' variant {}."), - variant - ); - res - })), - }, - }); - } - - let name = &self.ident; - // Output the token stream - quote! { - // Deserialize the data revision - let revision = ::deserialize_revisioned(reader)?; - // Deserialize the enum variant - let variant = ::deserialize_revisioned(reader)?; - // Output logic for this revision - match revision { - #deserializer - v => return Err(revision::Error::Deserialize({ - let res = format!( - concat!("Unknown '", stringify!(#name) ,"' variant {}."), - revision - ); - res - })), - } - } - } - - fn revision(&self) -> u16 { - self.revision - } - - fn reexpand(&self) -> proc_macro2::TokenStream { - let vis = &self.vis; - let ident = &self.ident; - let attrs = &self.attrs; - let fields = - self.fields.iter().filter(|x| x.exists_at(self.revision)).map(|e| e.reexpand()); - let generics = &self.generics; - - quote! { - #(#attrs)* - #vis enum #ident #generics { - #(#fields,)* - } - } - } -} diff --git a/derive/src/descriptors/mod.rs b/derive/src/descriptors/mod.rs deleted file mode 100644 index 5c139f2..0000000 --- a/derive/src/descriptors/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod enum_desc; -pub mod struct_desc; diff --git a/derive/src/descriptors/struct_desc.rs b/derive/src/descriptors/struct_desc.rs deleted file mode 100644 index e253975..0000000 --- a/derive/src/descriptors/struct_desc.rs +++ /dev/null @@ -1,164 +0,0 @@ -use crate::common::{Descriptor, GenericDescriptor, Kind}; -use crate::fields::struct_field::*; -use crate::fields::struct_index::*; -use crate::fields::struct_inner::*; -use crate::helpers::compute_revision; -use quote::{format_ident, quote}; - -pub(crate) type StructDescriptor = GenericDescriptor; - -impl StructDescriptor { - pub fn new(input: &syn::ItemStruct) -> Self { - // Create the new descriptor - let mut descriptor = StructDescriptor { - ident: input.ident.clone(), - vis: input.vis.clone(), - generics: input.generics.clone(), - attrs: input.attrs.clone(), - revision: 1, - fields: vec![], - kind: Kind::Struct, - }; - // Parse the struct fields - descriptor.parse_struct_fields(&input.fields); - // Compute the struct revision - descriptor.revision = compute_revision(&descriptor.fields); - // Check field attributes - for field in &descriptor.fields { - field.check_attributes(descriptor.revision); - } - // Return the descriptor - descriptor - } - - fn parse_struct_fields(&mut self, fields: &syn::Fields) { - match fields { - syn::Fields::Named(fields) => { - self.kind = Kind::Struct; - let pairs = fields.named.pairs(); - for (i, field) in pairs.enumerate() { - let field = field.value(); - self.fields.push(StructInner::StructField(StructField::new( - self.revision, - field, - i as u32, - ))); - } - } - syn::Fields::Unnamed(fields) => { - self.kind = Kind::Tuple; - let pairs = fields.unnamed.pairs(); - for (i, field) in pairs.enumerate() { - let field = field.value(); - self.fields.push(StructInner::StructIndex(StructIndex::new( - self.revision, - field, - i as u32, - ))); - } - } - syn::Fields::Unit => { - self.kind = Kind::Unit; - } - } - } -} - -impl Descriptor for StructDescriptor { - // Generate the serializer for this type - fn generate_serializer(&self) -> proc_macro2::TokenStream { - // Get the current revision - let revision = self.revision; - // Create a new token stream - let mut serializer = proc_macro2::TokenStream::new(); - // Extend the token stream for each field - for field in &self.fields { - serializer.extend(field.generate_serializer(self.revision)); - } - // Output the token stream - quote! { - revision::Revisioned::serialize_revisioned(&#revision, writer)?; - #serializer - Ok(()) - } - } - // Generate the deserializer for this type - fn generate_deserializer(&self) -> proc_macro2::TokenStream { - // Format the name of the struct - let ident = format_ident!("{}", self.ident); - // Create a new token stream - let mut deserializer = proc_macro2::TokenStream::new(); - // Extend the token stream for each revision - for i in 1..=self.revision { - // Create a new token stream for the struct revision `i`. - let mut outer = proc_macro2::TokenStream::new(); - let mut inner = proc_macro2::TokenStream::new(); - let mut after = proc_macro2::TokenStream::new(); - // Generate field and semantic deserializers for all fields. - for field in &self.fields { - let (o, i, a) = field.generate_deserializer(self.revision, i); - outer.extend(o); - inner.extend(i); - after.extend(a); - } - // Generate the deserializer match arm for revision `i`. - deserializer.extend(quote! { - #i => { - #outer - let mut object = #ident { - #inner - }; - #after - Ok(object) - } - }); - } - let name = &self.ident; - // Output the token stream - quote! { - // Deserialize the data revision - let revision = ::deserialize_revisioned(reader)?; - // Output logic for this revision - match revision { - #deserializer - v => return Err(revision::Error::Deserialize({ - let res = format!( - concat!("Unknown '", stringify!(#name) ,"' variant {}."), - revision - ); - res - })), - } - } - } - - fn revision(&self) -> u16 { - self.revision - } - - fn reexpand(&self) -> proc_macro2::TokenStream { - let vis = &self.vis; - let ident = &self.ident; - let attrs = &self.attrs; - let generics = &self.generics; - let fields = self.fields.iter().map(|e| e.reexpand()); - - match self.kind { - Kind::Unit => quote! { - #(#attrs)* - #vis struct #ident #generics; - }, - Kind::Tuple => quote! { - #(#attrs)* - #vis struct #ident #generics(#(#fields,)*); - }, - Kind::Struct => quote! { - #(#attrs)* - #vis struct #ident #generics { - #(#fields,)* - } - }, - _ => unreachable!(), - } - } -} diff --git a/derive/src/expand/common.rs b/derive/src/expand/common.rs new file mode 100644 index 0000000..7d92dcc --- /dev/null +++ b/derive/src/expand/common.rs @@ -0,0 +1,84 @@ +use std::collections::{HashMap, HashSet}; + +use syn::{Error, Ident}; + +use crate::ast::{self, Visit}; + +/// A pass which calculates discriminats for enum variants. +pub struct CalcDiscriminant<'a> { + revision: usize, + discriminants: &'a mut HashMap, + used: HashSet, + next: u32, +} + +impl<'a> CalcDiscriminant<'a> { + pub fn new(revision: usize, discriminants: &'a mut HashMap) -> Self { + Self { + revision, + discriminants, + used: HashSet::new(), + next: 0, + } + } +} + +impl<'a, 'ast> Visit<'ast> for CalcDiscriminant<'a> { + fn visit_enum(&mut self, i: &'ast crate::ast::Enum) -> syn::Result<()> { + GatherOverrides { + revision: self.revision, + discriminants: self.discriminants, + used: &mut self.used, + } + .visit_enum(i)?; + + ast::visit_enum(self, i) + } + + fn visit_variant(&mut self, i: &'ast ast::Variant) -> syn::Result<()> { + if !i.attrs.options.exists_at(self.revision) { + return Ok(()); + } + + if self.discriminants.contains_key(&i.ident) { + return Ok(()); + } + + while self.used.contains(&self.next) { + self.next += 1; + } + + self.used.insert(self.next); + self.discriminants.insert(i.ident.clone(), self.next); + Ok(()) + } +} + +pub struct GatherOverrides<'a> { + revision: usize, + discriminants: &'a mut HashMap, + used: &'a mut HashSet, +} + +impl<'a, 'ast> Visit<'ast> for GatherOverrides<'a> { + fn visit_variant(&mut self, i: &'ast crate::ast::Variant) -> syn::Result<()> { + if !i.attrs.options.exists_at(self.revision) { + return Ok(()); + } + + let Some(x) = i.attrs.options.overrides.get(&self.revision) else { + return Ok(()); + }; + + let Some(ref descr) = x.discriminant else { + return Ok(()); + }; + + if !self.used.insert(descr.value) { + return Err(Error::new(descr.span, "discriminant used twice for different variants")); + } + + self.discriminants.insert(i.ident.clone(), descr.value); + Ok(()) + } +} diff --git a/derive/src/expand/de.rs b/derive/src/expand/de.rs new file mode 100644 index 0000000..0dca938 --- /dev/null +++ b/derive/src/expand/de.rs @@ -0,0 +1,400 @@ +use std::collections::HashMap; + +use proc_macro2::{Span, TokenStream}; +use quote::{quote, TokenStreamExt}; +use syn::{Ident, Index}; + +use crate::ast::{Enum, Fields, Struct, Variant, Visit}; + +use super::common::CalcDiscriminant; + +/// Visitor which creates structs for fields in a an enum variant. +pub struct EnumStructsVisitor<'a> { + pub revision: usize, + pub stream: &'a mut TokenStream, +} + +impl<'a> EnumStructsVisitor<'a> { + pub fn new(revision: usize, stream: &'a mut TokenStream) -> Self { + Self { + revision, + stream, + } + } +} + +impl<'a, 'ast> Visit<'ast> for EnumStructsVisitor<'a> { + fn visit_enum(&mut self, i: &'ast Enum) -> syn::Result<()> { + for v in i.variants.iter() { + let name = v.fields_name(&i.name.to_string()); + + let new_struct = match v.fields { + Fields::Named { + ref fields, + .. + } => { + let fields = fields + .iter() + .filter(|x| x.attrs.options.exists_at(self.revision)) + .map(|x| { + let name = &x.name; + let ty = &x.ty; + quote! { + #name: #ty + } + }); + quote! { + struct #name{ #(#fields),* } + } + } + Fields::Unnamed { + ref fields, + .. + } => { + let fields = fields + .iter() + .filter(|x| x.attrs.options.exists_at(self.revision)) + .map(|x| &x.ty); + quote! { + struct #name( #(#fields),* ); + } + } + Fields::Unit => { + quote! { + #[allow(dead_code)] + struct #name; + } + } + }; + self.stream.append_all(new_struct); + } + Ok(()) + } +} + +pub struct DeserializeVisitor<'a> { + pub target: usize, + pub current: usize, + pub stream: &'a mut TokenStream, +} + +impl<'a, 'ast> Visit<'ast> for DeserializeVisitor<'a> { + fn visit_enum(&mut self, i: &'ast Enum) -> syn::Result<()> { + let mut discriminants = HashMap::new(); + CalcDiscriminant::new(self.current, &mut discriminants).visit_enum(i)?; + + let mut variants = TokenStream::new(); + DeserializeVariant { + name: i.name.clone(), + target: self.target, + current: self.current, + stream: &mut variants, + discriminants, + } + .visit_enum(i) + .unwrap(); + + let error_string = + format!("Invalid discriminant `{{x}}` for enum `{}` revision `{{__revision}}`", i.name); + + self.stream.append_all(quote! { + let __discriminant = ::deserialize_revisioned(reader)?; + match __discriminant { + #variants + x => { + return Err(::revision::Error::Deserialize( + format!(#error_string) + )) + } + } + }); + Ok(()) + } + + fn visit_struct(&mut self, i: &'ast Struct) -> syn::Result<()> { + let mut fields_binding = TokenStream::new(); + DeserializeFields { + target: self.target, + current: self.current, + stream: &mut fields_binding, + } + .visit_struct(i) + .unwrap(); + + match i.fields { + Fields::Named { + ref fields, + .. + } => { + self.stream.append_all(fields_binding); + + let bindings = fields + .iter() + .filter(|x| x.attrs.options.exists_at(self.target)) + .map(|x| x.name.to_binding()); + self.stream.append_all(quote! { + let mut __this = Self{ #(#bindings),* }; + }); + } + Fields::Unnamed { + ref fields, + .. + } => { + self.stream.append_all(fields_binding); + + let bindings = fields + .iter() + .filter(|x| x.attrs.options.exists_at(self.target)) + .map(|x| x.name.to_binding()); + self.stream.append_all(quote! { + let mut __this = Self( #(#bindings),* ); + }); + } + Fields::Unit => { + self.stream.append_all(quote! { + Ok(Self) + }); + return Ok(()); + } + } + + let (Fields::Named { + ref fields, + .. + } + | Fields::Unnamed { + ref fields, + .. + }) = i.fields + else { + unreachable!(); + }; + + for f in fields.iter().filter(|f| { + f.attrs.options.exists_at(self.current) && !f.attrs.options.exists_at(self.target) + }) { + let binding = f.name.to_binding(); + let convert = f.attrs.options.convert.as_ref().unwrap(); + let convert = Ident::new(&convert.value(), convert.span()); + let revision = self.current as u16; + self.stream.append_all(quote! { + Self::#convert(&mut __this,#revision,#binding)?; + }) + } + + self.stream.append_all(quote! { Ok(__this) }); + Ok(()) + } +} + +pub struct DeserializeVariant<'a> { + pub target: usize, + pub current: usize, + pub name: Ident, + pub stream: &'a mut TokenStream, + pub discriminants: HashMap, +} + +impl<'a, 'ast> Visit<'ast> for DeserializeVariant<'a> { + fn visit_variant(&mut self, i: &'ast Variant) -> syn::Result<()> { + let exists_current = i.attrs.options.exists_at(self.current); + let exists_target = i.attrs.options.exists_at(self.target); + + if !exists_current { + return Ok(()); + } + + let mut fields = TokenStream::new(); + DeserializeFields { + target: self.target, + current: self.current, + stream: &mut fields, + } + .visit_variant(i) + .unwrap(); + + let fields_struct_name = i.fields_name(&self.name.to_string()); + + let (bindings, create) = match i.fields { + Fields::Named { + ref fields, + .. + } => { + let mut bindings = TokenStream::new(); + let mut create = TokenStream::new(); + let field_names = fields + .iter() + .filter(|x| x.attrs.options.exists_at(self.target)) + .map(|x| x.name.to_binding()); + + let field_names_c = field_names.clone(); + bindings.append_all(quote! { + let mut __fields = #fields_struct_name{ #(#field_names_c),* }; + }); + + let variant_name = &i.ident; + create.append_all(quote! { + Ok(Self::#variant_name{ + #(#field_names: __fields.#field_names,)* + }) + }); + + for f in fields.iter().filter(|x| { + x.attrs.options.exists_at(self.current) + && !x.attrs.options.exists_at(self.target) + }) { + let binding = f.name.to_binding(); + let convert = f.attrs.options.convert.as_ref().unwrap(); + let convert = Ident::new(&convert.value(), convert.span()); + let revision = self.current as u16; + bindings.append_all(quote! { + Self::#convert(&mut __fields,#revision,#binding)?; + }) + } + (bindings, create) + } + Fields::Unnamed { + ref fields, + .. + } => { + let mut bindings = TokenStream::new(); + let mut create = TokenStream::new(); + let field_names = fields + .iter() + .filter(|x| x.attrs.options.exists_at(self.target)) + .map(|x| x.name.to_binding()); + + bindings.append_all(quote! { + let mut __fields = #fields_struct_name( #(#field_names),* ); + }); + + let field_names = fields + .iter() + .filter(|x| x.attrs.options.exists_at(self.target)) + .enumerate() + .map(|(idx, _)| Index { + index: idx as u32, + span: Span::call_site(), + }); + let variant_name = &i.ident; + create.append_all(quote! { + Ok(Self::#variant_name( #(__fields.#field_names,)*)) + }); + + for f in fields.iter().filter(|x| { + x.attrs.options.exists_at(self.current) + && !x.attrs.options.exists_at(self.target) + }) { + let binding = f.name.to_binding(); + let convert = f.attrs.options.convert.as_ref().unwrap(); + let convert = Ident::new(&convert.value(), convert.span()); + let revision = self.current as u16; + bindings.append_all(quote! { + Self::#convert(&mut __fields,#revision,#binding)?; + }) + } + (bindings, create) + } + Fields::Unit => { + let name = &i.ident; + ( + quote! { + let __fields = #fields_struct_name; + }, + quote! { + Ok(Self::#name) + }, + ) + } + }; + + if exists_target && exists_current { + let discr = self + .discriminants + .get(&i.ident) + .expect("missed variant during discriminant calculation"); + + self.stream.append_all(quote! { + #discr => { + #fields + #bindings + #create + } + }); + } else if !exists_target && exists_current { + let discr = self + .discriminants + .get(&i.ident) + .expect("missed variant during discriminant calculation"); + let convert = i.attrs.options.convert.as_ref().unwrap(); + let convert = Ident::new(&convert.value(), convert.span()); + let revision = self.current as u16; + + self.stream.append_all(quote! { + #discr => { + #fields + #bindings + + let __conv_fn: fn(#fields_struct_name, u16) -> ::std::result::Result = Self::#convert; + Self::#convert(__fields,#revision) + } + }) + } + + Ok(()) + } +} + +pub struct DeserializeFields<'a> { + pub target: usize, + pub current: usize, + pub stream: &'a mut TokenStream, +} + +impl<'a, 'ast> Visit<'ast> for DeserializeFields<'a> { + fn visit_fields(&mut self, i: &'ast Fields) -> syn::Result<()> { + match *i { + Fields::Named { + ref fields, + .. + } + | Fields::Unnamed { + ref fields, + .. + } => { + for f in fields.iter() { + let binding = f.name.to_binding(); + + let exists_current = f.attrs.options.exists_at(self.current); + let exists_target = f.attrs.options.exists_at(self.target); + + if exists_target && exists_current { + let ty = &f.ty; + self.stream.append_all(quote! { + let #binding = <#ty as ::revision::Revisioned>::deserialize_revisioned(reader)?; + }) + } else if exists_target && !exists_current { + if let Some(default) = f.attrs.options.default.as_ref() { + let default = Ident::new(&default.value(), default.span()); + let revision = self.current as u16; + self.stream.append_all(quote! { + let #binding = Self::#default(#revision)?; + }) + } else { + self.stream.append_all(quote! { + let #binding = Default::default(); + }) + } + } else if !exists_target && exists_current { + let ty = &f.ty; + self.stream.append_all(quote! { + let #binding = <#ty as ::revision::Revisioned>::deserialize_revisioned(reader)?; + }) + } + } + } + Fields::Unit => {} + } + Ok(()) + } +} diff --git a/derive/src/expand/mod.rs b/derive/src/expand/mod.rs new file mode 100644 index 0000000..928a8a3 --- /dev/null +++ b/derive/src/expand/mod.rs @@ -0,0 +1,120 @@ +mod common; +mod de; +mod reexport; +mod ser; +mod validate_version; + +use std::u16; + +use de::{DeserializeVisitor, EnumStructsVisitor}; +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use reexport::Reexport; +use ser::SerializeVisitor; +use validate_version::ValidateRevision; + +use crate::ast::{self, Direct, ItemOptions, Visit}; + +pub fn revision(attr: TokenStream, input: TokenStream) -> syn::Result { + let attrs: Direct = syn::parse2(attr)?; + let ast: ast::Item = syn::parse2(input)?; + + let revision = match (ast.attrs.options.revision, attrs.0.revision) { + (Some(x), None) | (None, Some(x)) => { + x + } + (None,None) => { + return Err(syn::Error::new(Span::call_site(),"Current revision not specified, please specify the current revision with `#[revisioned(revision = ..)]` ")) + } + (Some(_),Some(_)) => { + return Err(syn::Error::new(Span::call_site(),"Current revision specified twice")) + } + }; + + if revision > u16::MAX as usize { + return Err(syn::Error::new( + Span::call_site(), + format_args!("Revision exceeded maximum supported value of {}", u16::MAX), + )); + } + if revision == 0 { + return Err(syn::Error::new(Span::call_site(), "Revision versions start at 1")); + } + + // Make sure that all used revisions are less or equal to the current revision. + ValidateRevision(revision).visit_item(&ast)?; + + // Recreate the item. + let mut reexport = TokenStream::new(); + Reexport { + revision, + stream: &mut reexport, + } + .visit_item(&ast) + .unwrap(); + + // serialize implementation + let mut serialize = TokenStream::new(); + SerializeVisitor::new(revision, &mut serialize).visit_item(&ast).unwrap(); + + let mut deserialize_structs = TokenStream::new(); + EnumStructsVisitor::new(revision, &mut deserialize_structs).visit_item(&ast).unwrap(); + + // deserialize implementation + let deserialize = (1..=revision) + .map(|x| { + // one for every revision + let mut deserialize = TokenStream::new(); + DeserializeVisitor { + target: revision, + current: x, + stream: &mut deserialize, + } + .visit_item(&ast) + .unwrap(); + + let revision = x as u16; + + quote! { + #revision => { + #deserialize + } + } + }) + .collect::>(); + + let name = match ast.kind { + ast::ItemKind::Enum(x) => x.name, + ast::ItemKind::Struct(x) => x.name, + }; + let revision = revision as u16; + let revision_error = format!("Invalid revision `{{}}` for type `{}`", name); + + Ok(quote! { + #reexport + #deserialize_structs + + impl ::revision::Revisioned for #name { + fn revision() -> u16{ + #revision + } + + fn serialize_revisioned(&self, writer: &mut W) -> ::std::result::Result<(), ::revision::Error> { + ::revision::Revisioned::serialize_revisioned(&Self::revision(),writer)?; + #serialize + } + + fn deserialize_revisioned(reader: &mut R) -> ::std::result::Result { + let __revision = ::deserialize_revisioned(reader)?; + match __revision { + #(#deserialize)* + x => { + return Err(::revision::Error::Deserialize( + format!(#revision_error,x) + )) + } + } + } + } + }) +} diff --git a/derive/src/expand/reexport.rs b/derive/src/expand/reexport.rs new file mode 100644 index 0000000..11dd746 --- /dev/null +++ b/derive/src/expand/reexport.rs @@ -0,0 +1,153 @@ +use proc_macro2::{Span, TokenStream}; +use quote::ToTokens; +use syn::{punctuated::Pair, token}; + +use crate::ast::{self, Fields, Visit}; + +/// Visitor which reexports the item, recreating it with only the given fields. +pub struct Reexport<'a> { + pub revision: usize, + pub stream: &'a mut TokenStream, +} +impl<'a, 'ast> Visit<'ast> for Reexport<'a> { + fn visit_item(&mut self, i: &'ast ast::Item) -> syn::Result<()> { + for attr in i.attrs.other.iter() { + attr.to_tokens(self.stream) + } + i.vis.to_tokens(self.stream); + ast::visit_item(self, i) + } + + fn visit_item_kind(&mut self, i: &'ast ast::ItemKind) -> syn::Result<()> { + ast::visit_item_kind(self, i) + } + + fn visit_enum(&mut self, i: &'ast ast::Enum) -> syn::Result<()> { + i.enum_.to_tokens(self.stream); + i.name.to_tokens(self.stream); + i.generics.to_tokens(self.stream); + i.braces.surround(self.stream, |stream| { + let mut this = Reexport { + revision: self.revision, + stream, + }; + for pairs in i.variants.pairs() { + match pairs { + Pair::Punctuated(v, p) => { + if v.attrs.options.exists_at(self.revision) { + this.visit_variant(v).unwrap(); + p.to_tokens(this.stream); + } + } + Pair::End(v) => { + if v.attrs.options.exists_at(self.revision) { + this.visit_variant(v).unwrap(); + } + } + } + } + }); + Ok(()) + } + + fn visit_struct(&mut self, i: &'ast ast::Struct) -> syn::Result<()> { + i.struct_.to_tokens(self.stream); + i.name.to_tokens(self.stream); + i.generics.to_tokens(self.stream); + ast::visit_struct(self, i)?; + if matches!(i.fields, Fields::Unnamed { .. } | Fields::Unit) { + token::Semi(Span::call_site()).to_tokens(self.stream); + } + + Ok(()) + } + + fn visit_variant(&mut self, i: &'ast ast::Variant) -> syn::Result<()> { + if !i.attrs.options.exists_at(self.revision) { + return Ok(()); + } + + i.attrs.other.iter().for_each(|x| x.to_tokens(self.stream)); + i.ident.to_tokens(self.stream); + ast::visit_variant(self, i)?; + + if let Some((eq, expr)) = i.discriminant.as_ref() { + eq.to_tokens(self.stream); + expr.to_tokens(self.stream); + } + + Ok(()) + } + + fn visit_fields(&mut self, i: &'ast ast::Fields) -> syn::Result<()> { + match i { + ast::Fields::Named { + brace, + fields, + } => { + brace.surround(self.stream, |stream| { + let mut this = Reexport { + revision: self.revision, + stream, + }; + for pair in fields.pairs() { + match pair { + Pair::Punctuated(f, c) => { + if f.attrs.options.exists_at(self.revision) { + this.visit_field(f).unwrap(); + c.to_tokens(this.stream) + } + } + Pair::End(f) => { + if f.attrs.options.exists_at(self.revision) { + this.visit_field(f).unwrap(); + } + } + } + } + }); + Ok(()) + } + ast::Fields::Unnamed { + paren, + fields, + } => { + paren.surround(self.stream, |stream| { + let mut this = Reexport { + revision: self.revision, + stream, + }; + for pair in fields.pairs() { + match pair { + Pair::Punctuated(f, c) => { + if f.attrs.options.exists_at(self.revision) { + this.visit_field(f).unwrap(); + c.to_tokens(this.stream) + } + } + Pair::End(f) => { + if f.attrs.options.exists_at(self.revision) { + this.visit_field(f).unwrap(); + } + } + } + } + }); + Ok(()) + } + ast::Fields::Unit => Ok(()), + } + } + + fn visit_field(&mut self, i: &'ast ast::Field) -> syn::Result<()> { + i.attrs.other.iter().for_each(|x| x.to_tokens(self.stream)); + i.vis.to_tokens(self.stream); + match i.name { + ast::FieldName::Ident(ref x) => x.to_tokens(self.stream), + ast::FieldName::Index(_) => {} + } + i.colon_token.map(|x| x.to_tokens(self.stream)); + i.ty.to_tokens(self.stream); + Ok(()) + } +} diff --git a/derive/src/expand/ser.rs b/derive/src/expand/ser.rs new file mode 100644 index 0000000..85f087c --- /dev/null +++ b/derive/src/expand/ser.rs @@ -0,0 +1,208 @@ +use proc_macro2::{Span, TokenStream}; +use quote::{quote, TokenStreamExt}; +use std::collections::HashMap; +use syn::Ident; + +use crate::ast::{Enum, Field, Fields, Struct, Variant, Visit}; + +use super::common::CalcDiscriminant; + +pub struct SerializeVisitor<'a> { + pub revision: usize, + pub stream: &'a mut TokenStream, +} + +impl<'a> SerializeVisitor<'a> { + pub fn new(revision: usize, stream: &'a mut TokenStream) -> Self { + Self { + revision, + stream, + } + } +} + +impl<'a, 'ast> Visit<'ast> for SerializeVisitor<'a> { + fn visit_struct(&mut self, i: &'ast Struct) -> syn::Result<()> { + let mut ser_fields = TokenStream::new(); + SerializeFields { + revision: self.revision, + stream: &mut ser_fields, + } + .visit_struct(i) + .unwrap(); + + match i.fields { + Fields::Named { + ref fields, + .. + } => { + for f in fields.iter().filter(|x| x.attrs.options.exists_at(self.revision)) { + let name = &f.name; + self.stream.append_all(quote! { let #name = &self.#name; }); + } + self.stream.append_all(ser_fields); + } + Fields::Unnamed { + ref fields, + .. + } => { + for (idx, f) in + fields.iter().filter(|x| x.attrs.options.exists_at(self.revision)).enumerate() + { + let binding = f.name.to_binding(); + let idx = syn::Index { + index: idx as u32, + span: Span::call_site(), + }; + self.stream.append_all(quote! { let #binding = &self.#idx; }); + } + self.stream.append_all(ser_fields); + } + Fields::Unit => {} + } + self.stream.append_all(quote! { Ok(()) }); + Ok(()) + } + + fn visit_enum(&mut self, i: &'ast Enum) -> syn::Result<()> { + let mut discriminants = HashMap::new(); + CalcDiscriminant::new(self.revision, &mut discriminants).visit_enum(i)?; + + let mut ser_variants = TokenStream::new(); + SerializeVariant { + revision: self.revision, + discriminants, + stream: &mut ser_variants, + } + .visit_enum(i) + .unwrap(); + + self.stream.append_all(quote! { + match *self{ + #ser_variants + } + }); + + Ok(()) + } + + fn visit_field(&mut self, i: &'ast Field) -> syn::Result<()> { + let name = &i.name; + + self.stream.append_all(quote! { + ::revision::Revisioned::serialize_revisioned(#name,writer)?; + }); + + Ok(()) + } +} + +pub struct SerializeFields<'a> { + pub revision: usize, + pub stream: &'a mut TokenStream, +} + +impl<'a, 'ast> Visit<'ast> for SerializeFields<'a> { + fn visit_field(&mut self, i: &'ast Field) -> syn::Result<()> { + if !i.attrs.options.exists_at(self.revision) { + return Ok(()); + } + + let name = i.name.to_binding(); + self.stream.append_all(quote! { + ::revision::Revisioned::serialize_revisioned(#name,writer)?; + }); + + Ok(()) + } +} + +pub struct SerializeVariant<'a> { + pub revision: usize, + pub discriminants: HashMap, + pub stream: &'a mut TokenStream, +} + +impl<'a, 'ast> Visit<'ast> for SerializeVariant<'a> { + fn visit_variant(&mut self, i: &'ast Variant) -> syn::Result<()> { + if !i.attrs.options.exists_at(self.revision) { + return Ok(()); + } + + let name = &i.ident; + + self.stream.append_all(quote! {Self::#name}); + + let discr = + self.discriminants.get(name).expect("missed variant during discriminants calculation"); + + match i.fields { + Fields::Named { + ref fields, + .. + } => { + let bindings = fields + .iter() + .filter(|x| x.attrs.options.exists_at(self.revision)) + .map(|x| &x.name); + self.stream.append_all(quote! { + { #(ref #bindings),* } + }); + + let mut fields_ser = TokenStream::new(); + + SerializeFields { + revision: self.revision, + stream: &mut fields_ser, + } + .visit_variant(i) + .unwrap(); + + self.stream.append_all(quote! { + => { + ::revision::Revisioned::serialize_revisioned(&#discr,writer)?; + #fields_ser + Ok(()) + }, + }) + } + Fields::Unnamed { + ref fields, + .. + } => { + let bindings = fields + .iter() + .filter(|x| x.attrs.options.exists_at(self.revision)) + .map(|x| x.name.to_binding()); + self.stream.append_all(quote! { + ( #(ref #bindings),* ) + }); + + let mut fields_ser = TokenStream::new(); + + SerializeFields { + revision: self.revision, + stream: &mut fields_ser, + } + .visit_variant(i) + .unwrap(); + + self.stream.append_all(quote! { + => { + ::revision::Revisioned::serialize_revisioned(&#discr,writer)?; + #fields_ser + Ok(()) + } + }) + } + Fields::Unit => { + self.stream.append_all(quote! { => { + ::revision::Revisioned::serialize_revisioned(&#discr,writer)?; + Ok(()) + }}); + } + } + + Ok(()) + } +} diff --git a/derive/src/expand/validate_version.rs b/derive/src/expand/validate_version.rs new file mode 100644 index 0000000..842697e --- /dev/null +++ b/derive/src/expand/validate_version.rs @@ -0,0 +1,32 @@ +use crate::ast::{self, Visit}; + +pub struct ValidateRevision(pub usize); +impl<'ast> Visit<'ast> for ValidateRevision { + fn visit_field(&mut self, i: &'ast ast::Field) -> syn::Result<()> { + if let Some(s) = i.attrs.options.start.as_ref() { + if s.value > self.0 { + return Err(syn::Error::new(s.span, "used revision exceededs current revision")); + } + } + if let Some(s) = i.attrs.options.end.as_ref() { + if s.value > self.0 { + return Err(syn::Error::new(s.span, "used revision exceededs current revision")); + } + } + Ok(()) + } + + fn visit_variant(&mut self, i: &'ast ast::Variant) -> syn::Result<()> { + if let Some(s) = i.attrs.options.start.as_ref() { + if s.value > self.0 { + return Err(syn::Error::new(s.span, "used revision exceededs current revision")); + } + } + if let Some(s) = i.attrs.options.end.as_ref() { + if s.value > self.0 { + return Err(syn::Error::new(s.span, "used revision exceededs current revision")); + } + } + ast::visit_variant(self, i) + } +} diff --git a/derive/src/fields/enum_inner.rs b/derive/src/fields/enum_inner.rs deleted file mode 100644 index 23bfdc8..0000000 --- a/derive/src/fields/enum_inner.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::enum_struct::*; -use super::enum_tuple::*; -use crate::common::Exists; -use proc_macro2::TokenStream; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub(crate) enum EnumInner { - EnumTuple(EnumTuple), - EnumStruct(EnumStruct), -} - -impl Exists for EnumInner { - fn start_revision(&self) -> u16 { - match self { - Self::EnumTuple(v) => v.start_revision(), - Self::EnumStruct(v) => v.start_revision(), - } - } - fn end_revision(&self) -> Option { - match self { - Self::EnumTuple(v) => v.end_revision(), - Self::EnumStruct(v) => v.end_revision(), - } - } - fn sub_revision(&self) -> u16 { - match self { - Self::EnumTuple(v) => v.sub_revision(), - Self::EnumStruct(v) => v.sub_revision(), - } - } -} - -impl EnumInner { - pub fn check_attributes(&self, current: u16) { - match self { - Self::EnumTuple(v) => v.check_attributes(current), - Self::EnumStruct(v) => v.check_attributes(current), - } - } - pub fn generate_serializer(&self, current: u16) -> TokenStream { - match self { - Self::EnumTuple(v) => v.generate_serializer(current), - Self::EnumStruct(v) => v.generate_serializer(current), - } - } - pub fn generate_deserializer(&self, current: u16, revision: u16) -> TokenStream { - match self { - Self::EnumTuple(v) => v.generate_deserializer(current, revision), - Self::EnumStruct(v) => v.generate_deserializer(current, revision), - } - } - - pub fn reexpand(&self) -> TokenStream { - match self { - Self::EnumTuple(v) => v.reexpand(), - Self::EnumStruct(v) => v.reexpand(), - } - } -} diff --git a/derive/src/fields/enum_struct.rs b/derive/src/fields/enum_struct.rs deleted file mode 100644 index 2903ba2..0000000 --- a/derive/src/fields/enum_struct.rs +++ /dev/null @@ -1,148 +0,0 @@ -use super::ParsedEnumVariant; -use crate::common::Exists; -use crate::fields::enum_struct_field::*; -use darling::FromVariant; -use proc_macro2::TokenStream; -use proc_macro_error::abort; -use quote::quote; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub(crate) struct EnumStruct { - revision: u16, - index: u32, - parsed: ParsedEnumVariant, - // fields: Vec<(syn::Ident, syn::Type)>, - fields: Vec, -} - -impl Exists for EnumStruct { - fn start_revision(&self) -> u16 { - self.parsed.start.unwrap_or(self.revision) - } - fn end_revision(&self) -> Option { - self.parsed.end - } - fn sub_revision(&self) -> u16 { - let mut revision = 1; - for field in self.fields.iter() { - revision = revision.max(field.start_revision()).max(field.end_revision().unwrap_or(0)); - } - revision - } -} - -impl EnumStruct { - pub fn new(revision: u16, variant: &syn::Variant, index: u32) -> Self { - // Parse the field macro attributes - let parsed = match ParsedEnumVariant::from_variant(variant) { - Ok(x) => x, - Err(e) => { - abort!(variant.ident.span(), "{}", e); - } - }; - // Process the enum variant fields - let fields = match &variant.fields { - // syn::Fields::Named(fields) => fields - // .named - // .iter() - // .map(|field| (field.ident.clone().unwrap(), field.ty.clone())) - // .collect(), - syn::Fields::Named(fields) => fields - .named - .iter() - .enumerate() - .map(|(i, field)| StructField::new(revision, field, i as u32)) - .collect(), - _ => Vec::new(), - }; - // Create the enum variant holder - EnumStruct { - revision, - index, - fields, - parsed, - } - } - - pub fn reexpand(&self) -> TokenStream { - let ident = &self.parsed.ident; - let attrs = &self.parsed.attrs; - let fields = self.fields.iter().map(|x| x.reexpand()); - quote!( - #(#attrs)* - #ident{ #(#fields,)* } - ) - } - - pub fn check_attributes(&self, current: u16) { - if !self.exists_at(current) && self.parsed.convert_fn.is_none() { - abort!( - self.parsed.ident.span(), - "Expected a 'convert_fn' to be specified for enum variant {}", - self.parsed.ident - ); - } - // Check field attributes - for field in &self.fields { - field.check_attributes(current); - } - } - - pub fn generate_serializer(&self, revision: u16) -> TokenStream { - // Get the variant identifier - let field_ident = &self.parsed.ident; - // Get the variant index - let index = self.index; - // Create a token stream for the serializer - let mut serializer = TokenStream::new(); - // Create a token stream for the variant fields - let mut inner = TokenStream::new(); - // Loop over each of the enum variant fields - for field in &self.fields { - // Get the field identifier - let name = field.name(); - // Extend the enum constructor - inner.extend(quote!(#name,)); - // Extend the serializer - serializer.extend(field.generate_serializer(revision)); - } - // Output the token stream - quote! { - Self::#field_ident{#inner} => { - revision::Revisioned::serialize_revisioned(&#index, writer)?; - #serializer - }, - } - } - - pub fn generate_deserializer(&self, current: u16, revision: u16) -> TokenStream { - // Get the variant index - let index = self.index; - // Get the variant identifier - let ident = &self.parsed.ident; - // Check if the variant is new. - if !self.exists_at(revision) { - return quote!(); - } - // Create a new token stream for the struct revision `i`. - let mut outer = proc_macro2::TokenStream::new(); - let mut inner = proc_macro2::TokenStream::new(); - let mut after = proc_macro2::TokenStream::new(); - // Loop over each of the enum variant fields - for field in &self.fields { - let (o, i, a) = field.generate_deserializer(current, revision); - outer.extend(o); - inner.extend(i); - after.extend(a); - } - // Output the token stream - quote! { - #index => { - #outer - let mut object = Self::#ident{#inner}; - #after - Ok(object) - }, - } - } -} diff --git a/derive/src/fields/enum_struct_field.rs b/derive/src/fields/enum_struct_field.rs deleted file mode 100644 index e163c3a..0000000 --- a/derive/src/fields/enum_struct_field.rs +++ /dev/null @@ -1,172 +0,0 @@ -use super::ParsedField; -use crate::common::Exists; -use darling::FromField; -use proc_macro2::{Span, TokenStream}; -use proc_macro_error::abort; -use quote::quote; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub(crate) struct StructField { - revision: u16, - parsed: ParsedField, -} - -impl Exists for StructField { - fn start_revision(&self) -> u16 { - self.parsed.start.unwrap_or(self.revision) - } - fn end_revision(&self) -> Option { - self.parsed.end - } - fn sub_revision(&self) -> u16 { - 0 - } -} - -impl StructField { - pub fn new(revision: u16, field: &syn::Field, _: u32) -> Self { - // Parse the field macro attributes - let parsed = match ParsedField::from_field(field) { - Ok(x) => x, - Err(e) => { - abort!(e.span(), "{e}") - } - }; - // Create the struct field holder - StructField { - revision, - parsed, - } - } - - pub fn reexpand(&self) -> TokenStream { - let vis = &self.parsed.vis; - let ident = self.parsed.ident.as_ref().unwrap(); - let ty = &self.parsed.ty; - let attrs = &self.parsed.attrs; - quote!( - #(#attrs)* - #vis #ident: #ty - ) - } - - pub fn name(&self) -> syn::Ident { - self.parsed.ident.clone().unwrap() - } - - pub fn check_attributes(&self, current: u16) { - if !self.exists_at(current) && self.parsed.convert_fn.is_none() { - let ident = self.parsed.ident.as_ref().unwrap(); - abort!(ident.span(), "Expected a 'convert_fn' to be specified for field {}", ident); - } - } - - pub fn generate_serializer(&self, current: u16) -> TokenStream { - // Get the field identifier - let field = self.parsed.ident.as_ref().unwrap(); - // Check if this field exists for this revision - if !self.exists_at(current) { - return TokenStream::new(); - } - // Match the type of the field. - match &self.parsed.ty { - syn::Type::Array(_) => quote! { - for element in #field.iter() { - revision::Revisioned::serialize_revisioned(element, writer)?; - } - }, - syn::Type::Path(_) => quote! { - revision::Revisioned::serialize_revisioned(#field, writer)?; - }, - syn::Type::Reference(_) => quote! { - #field.serialize_revisioned(writer)?; - }, - v => panic!("Unsupported field type {v:?}"), - } - } - - pub fn generate_deserializer( - &self, - current: u16, - revision: u16, - ) -> (TokenStream, TokenStream, TokenStream) { - // Get the field type. - let kind = &self.parsed.ty; - // Get the field identifier. - let field = self.parsed.ident.as_ref().unwrap(); - // If the field didn't exist, use default annotation or Default trait. - if !self.exists_at(revision) { - return self.generate_deserializer_newfield(); - } - // If the field did exist, but no longer does, use convert annotation if specified. - if self.exists_at(revision) && !self.exists_at(current) { - return self.generate_deserializer_oldfield(); - } - // Output the token streams - ( - // Deserialize the field from the reader - quote! { - let #field = <#kind as revision::Revisioned>::deserialize_revisioned(reader)?; - }, - // Insert the field value into the struct - quote! { - #field, - }, - // No need for any field post-processing - quote! {}, - ) - } - - fn generate_deserializer_newfield(&self) -> (TokenStream, TokenStream, TokenStream) { - // Get the field identifier. - let field = self.parsed.ident.as_ref().unwrap(); - // Output the token streams - ( - // Field did not exist, so don't deserialize it - quote! {}, - // Set the field default value on the struct - match &self.parsed.default_fn { - Some(default_fn) => { - let default_fn = syn::Ident::new(default_fn, Span::call_site()); - quote! { - #field: Self::#default_fn(revision), - } - } - None => quote! { - #field: Default::default(), - }, - }, - // No need for any field post-processing - quote! {}, - ) - } - - fn generate_deserializer_oldfield(&self) -> (TokenStream, TokenStream, TokenStream) { - // Get the field type. - let kind = &self.parsed.ty; - // Get the field identifier. - let field = self.parsed.ident.as_ref().unwrap(); - // Output the token streams - ( - // Deserialize the field which no longer exists - quote! { - let #field = <#kind as revision::Revisioned>::deserialize_revisioned(reader)?; - }, - // Don't insert the field into the current struct - quote! { - // TODO: remove this field entirely using proc macro - #field: Default::default(), - }, - // Post process the field data with the struct - match &self.parsed.convert_fn { - Some(convert_fn) => { - let convert_fn = syn::Ident::new(convert_fn, Span::call_site()); - quote! { - object.#convert_fn(revision, #field)?; - } - } - None => quote! {}, - }, - ) - } -} diff --git a/derive/src/fields/enum_tuple.rs b/derive/src/fields/enum_tuple.rs deleted file mode 100644 index 99ddd5d..0000000 --- a/derive/src/fields/enum_tuple.rs +++ /dev/null @@ -1,194 +0,0 @@ -use super::ParsedEnumVariant; -use crate::common::Exists; -use darling::FromVariant; -use proc_macro2::{Span, TokenStream}; -use proc_macro_error::abort; -use quote::{format_ident, quote}; -use syn::spanned::Spanned; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub(crate) struct EnumTuple { - revision: u16, - index: u32, - is_unit: bool, - parsed: ParsedEnumVariant, -} - -impl Exists for EnumTuple { - fn start_revision(&self) -> u16 { - self.parsed.start.unwrap_or(self.revision) - } - fn end_revision(&self) -> Option { - self.parsed.end - } - fn sub_revision(&self) -> u16 { - 0 - } -} - -impl EnumTuple { - pub fn new(revision: u16, variant: &syn::Variant, index: u32) -> Self { - // Parse the variant macro attributes - let parsed = match ParsedEnumVariant::from_variant(variant) { - Ok(x) => x, - Err(e) => { - abort!(variant.ident.span(), "{}", e); - } - }; - - for f in parsed.fields.iter() { - if f.end.is_some() - || f.start.is_some() - || f.default_fn.is_some() - || f.convert_fn.is_some() - { - abort!( - f.ty.span(), - "Revision attributes are not yet supported on enum variant fields" - ) - } - } - - let is_unit = matches!(variant.fields, syn::Fields::Unit); - - // Create the enum variant holder - EnumTuple { - revision, - index, - parsed, - is_unit, - } - } - - pub fn reexpand(&self) -> TokenStream { - let ident = &self.parsed.ident; - let attrs = &self.parsed.attrs; - if self.is_unit { - quote!( - #(#attrs)* - #ident - ) - } else { - let fields = self.parsed.fields.iter().map(|x| { - let attr = &x.attrs; - let ty = &x.ty; - quote!( - #(#attr)* #ty - ) - }); - quote!( - #(#attrs)* - #ident( #(#fields,)* ) - ) - } - } - - pub fn check_attributes(&self, current: u16) { - if !self.exists_at(current) && self.parsed.convert_fn.is_none() { - abort!( - self.parsed.ident.span(), - "Expected a 'convert_fn' to be specified for enum variant {}", - self.parsed.ident - ); - } - } - - pub fn generate_serializer(&self, current: u16) -> TokenStream { - // Get the variant index - let index = self.index; - // Get the variant identifier - let ident = &self.parsed.ident; - // Create a token stream for the serializer - let mut serializer = TokenStream::new(); - // Create a token stream for the variant fields - let mut inner = TokenStream::new(); - // Loop over each of the enum variant fields - for (index, _) in self.parsed.fields.iter().enumerate() { - // Get the field identifier - let field = format_ident!("v{}", index); - // Extend the enum constructor - inner.extend(quote!(#field,)); - // Extend the serializer - serializer.extend(quote! { - revision::Revisioned::serialize_revisioned(#field, writer)?; - }); - } - // Output the token stream - if self.parsed.fields.is_empty() { - if !self.exists_at(current) { - panic!("tried to generate a serializer a field which was deleted."); - } else { - quote! { - Self::#ident => { - revision::Revisioned::serialize_revisioned(&#index, writer)?; - }, - } - } - } else if !self.exists_at(current) { - panic!("tried to generate a serializer a field which was deleted."); - } else { - quote! { - Self::#ident(#inner) => { - revision::Revisioned::serialize_revisioned(&#index, writer)?; - #serializer - }, - } - } - } - - pub fn generate_deserializer(&self, current: u16, revision: u16) -> TokenStream { - // Get the variant index - let index = self.index; - // Get the variant identifier - let ident = &self.parsed.ident; - // Check if the variant is new. - if !self.exists_at(revision) { - return quote!(); - } - // Create a token stream for the field deserialisation - let mut deserializer = TokenStream::new(); - // Create a token stream for the fields - let mut inner = TokenStream::new(); - // Loop over the enum variant fields - for (index, f) in self.parsed.fields.iter().enumerate() { - // Get the field identifier - let field = format_ident!("v{}", index); - let kind = &f.ty; - // Extend the enum constructor - inner.extend(quote!(#field,)); - // Extend the deserializer - deserializer.extend(quote! { - let #field = <#kind as revision::Revisioned>::deserialize_revisioned(reader)?; - }); - } - // Check if the variant no longer exists - if !self.exists_at(current) { - // Get the conversion function - let convert_fn = - syn::Ident::new(self.parsed.convert_fn.as_ref().unwrap(), Span::call_site()); - // Output the - quote! { - #index => { - #deserializer - return Self::#convert_fn(revision, (#inner)); - }, - } - } else { - // Check if this is a simple enum - if self.parsed.fields.is_empty() { - quote! { - #index => { - return Ok(Self::#ident); - }, - } - } else { - quote! { - #index => { - #deserializer - return Ok(Self::#ident(#inner)); - }, - } - } - } - } -} diff --git a/derive/src/fields/mod.rs b/derive/src/fields/mod.rs deleted file mode 100644 index 4faef79..0000000 --- a/derive/src/fields/mod.rs +++ /dev/null @@ -1,45 +0,0 @@ -use darling::{FromField, FromVariant}; - -pub mod enum_inner; -pub mod enum_struct; -pub mod enum_struct_field; -pub mod enum_tuple; -pub mod struct_field; -pub mod struct_index; -pub mod struct_inner; - -/// A parsed struct field alongside its attributes. -#[derive(Debug, Eq, PartialEq, Clone, FromField)] -#[darling(attributes(revision), forward_attrs)] -pub struct ParsedField { - ident: Option, - ty: syn::Type, - vis: syn::Visibility, - #[darling(default)] - start: Option, - #[darling(default)] - end: Option, - #[darling(default)] - default_fn: Option, - #[darling(default)] - convert_fn: Option, - attrs: Vec, -} - -/// A parsed enum variant alongside its attributes. -#[derive(Debug, Eq, PartialEq, Clone, FromVariant)] -#[darling(attributes(revision), forward_attrs)] -struct ParsedEnumVariant { - ident: syn::Ident, - discriminant: Option, - #[darling(default)] - start: Option, - #[darling(default)] - end: Option, - #[darling(default)] - default_fn: Option, - #[darling(default)] - convert_fn: Option, - fields: darling::ast::Fields, - attrs: Vec, -} diff --git a/derive/src/fields/struct_field.rs b/derive/src/fields/struct_field.rs deleted file mode 100644 index 8459a76..0000000 --- a/derive/src/fields/struct_field.rs +++ /dev/null @@ -1,173 +0,0 @@ -use crate::common::Exists; -use darling::FromField; -use proc_macro2::{Span, TokenStream}; -use proc_macro_error::abort; -use quote::quote; -use syn::spanned::Spanned; - -use super::ParsedField; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub(crate) struct StructField { - revision: u16, - parsed: ParsedField, -} - -impl Exists for StructField { - fn start_revision(&self) -> u16 { - self.parsed.start.unwrap_or(self.revision) - } - fn end_revision(&self) -> Option { - self.parsed.end - } - fn sub_revision(&self) -> u16 { - 0 - } -} - -impl StructField { - pub fn new(revision: u16, field: &syn::Field, _: u32) -> Self { - // Parse the field macro attributes - let parsed = match ParsedField::from_field(field) { - Ok(x) => x, - Err(e) => { - abort!(e.span(), "{e}") - } - }; - - assert!(parsed.ident.is_some(), "tried to parse a tuple field as a named field"); - - // Create the struct field holder - StructField { - revision, - parsed, - } - } - - pub fn reexpand(&self) -> TokenStream { - let vis = &self.parsed.vis; - let ident = &self.parsed.ident; - let ty = &self.parsed.ty; - let attrs = &self.parsed.attrs; - quote!( - #(#attrs)* - #vis #ident: #ty - ) - } - - pub fn check_attributes(&self, current: u16) { - if !self.exists_at(current) && self.parsed.convert_fn.is_none() { - let ident = self.parsed.ident.as_ref().unwrap(); - abort!(ident.span(), "Expected a 'convert_fn' to be specified for field {}", ident); - } - } - - pub fn generate_serializer(&self, current: u16) -> TokenStream { - // Get the field identifier - let field = self.parsed.ident.as_ref().unwrap(); - // Check if this field exists for this revision - if !self.exists_at(current) { - return TokenStream::new(); - } - // Match the type of the field. - match &self.parsed.ty { - syn::Type::Array(_) => quote! { - for element in self.#field.iter() { - revision::Revisioned::serialize_revisioned(element, writer)?; - } - }, - syn::Type::Path(_) => quote! { - revision::Revisioned::serialize_revisioned(&self.#field, writer)?; - }, - syn::Type::Reference(_) => quote! { - self.#field.serialize_revisioned(writer)?; - }, - v => abort!(v.span(), "Unsupported field type"), - } - } - - pub fn generate_deserializer( - &self, - current: u16, - revision: u16, - ) -> (TokenStream, TokenStream, TokenStream) { - // Get the field type. - let kind = &self.parsed.ty; - // Get the field identifier. - let field = self.parsed.ident.as_ref().unwrap(); - // If the field didn't exist, use default annotation or Default trait. - if !self.exists_at(revision) { - return self.generate_deserializer_newfield(); - } - // If the field did exist, but no longer does, use convert annotation if specified. - if self.exists_at(revision) && !self.exists_at(current) { - return self.generate_deserializer_oldfield(); - } - // Output the token streams - ( - // Deserialize the field from the reader - quote! { - let #field = <#kind as revision::Revisioned>::deserialize_revisioned(reader)?; - }, - // Insert the field value into the struct - quote! { - #field, - }, - // No need for any field post-processing - quote! {}, - ) - } - - fn generate_deserializer_newfield(&self) -> (TokenStream, TokenStream, TokenStream) { - // Get the field identifier. - let field = self.parsed.ident.as_ref().unwrap(); - // Output the token streams - ( - // Field did not exist, so don't deserialize it - quote! {}, - // Set the field default value on the struct - match &self.parsed.default_fn { - Some(default_fn) => { - let default_fn = syn::Ident::new(default_fn, Span::call_site()); - quote! { - #field: Self::#default_fn(revision), - } - } - None => quote! { - #field: Default::default(), - }, - }, - // No need for any field post-processing - quote! {}, - ) - } - - fn generate_deserializer_oldfield(&self) -> (TokenStream, TokenStream, TokenStream) { - // Get the field type. - let kind = &self.parsed.ty; - // Get the field identifier. - let field = self.parsed.ident.as_ref().unwrap(); - // Output the token streams - ( - // Deserialize the field which no longer exists - quote! { - let #field = <#kind as revision::Revisioned>::deserialize_revisioned(reader)?; - }, - // Don't insert the field into the current struct - quote! { - // TODO: remove this field entirely using proc macro - #field: Default::default(), - }, - // Post process the field data with the struct - match &self.parsed.convert_fn { - Some(convert_fn) => { - let convert_fn = syn::Ident::new(convert_fn, Span::call_site()); - quote! { - object.#convert_fn(revision, #field)?; - } - } - None => quote! {}, - }, - ) - } -} diff --git a/derive/src/fields/struct_index.rs b/derive/src/fields/struct_index.rs deleted file mode 100644 index fdd7720..0000000 --- a/derive/src/fields/struct_index.rs +++ /dev/null @@ -1,178 +0,0 @@ -use super::ParsedField; -use crate::common::Exists; -use darling::FromField; -use proc_macro2::{Span, TokenStream}; -use proc_macro_error::abort; -use quote::{format_ident, quote}; -use syn::spanned::Spanned; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub(crate) struct StructIndex { - index: u32, - revision: u16, - parsed: ParsedField, -} - -impl Exists for StructIndex { - fn start_revision(&self) -> u16 { - self.parsed.start.unwrap_or(self.revision) - } - fn end_revision(&self) -> Option { - self.parsed.end - } - fn sub_revision(&self) -> u16 { - 0 - } -} - -impl StructIndex { - pub fn new(revision: u16, field: &syn::Field, index: u32) -> Self { - // Parse the field macro attributes - let parsed = match ParsedField::from_field(field) { - Ok(x) => x, - Err(e) => { - abort!(e.span(), "{e}") - } - }; - - assert!(parsed.ident.is_none(), "tried to parse a named field as a tuple field"); - - // Create the struct field holder - StructIndex { - index, - revision, - parsed, - } - } - - pub fn reexpand(&self) -> TokenStream { - let vis = &self.parsed.vis; - let ty = &self.parsed.ty; - let attrs = &self.parsed.attrs; - quote!( - #(#attrs)* #vis #ty - ) - } - - pub fn check_attributes(&self, current: u16) { - if !self.exists_at(current) && self.parsed.convert_fn.is_none() { - abort!( - self.parsed.ty.span(), - "Expected a 'convert_fn' to be specified for field {}", - self.index - ); - } - } - - pub fn generate_serializer(&self, current: u16) -> TokenStream { - // Get the field identifier. - let field = syn::Index::from(self.index as usize); - // Check if this field exists for this revision. - if !self.exists_at(current) { - return proc_macro2::TokenStream::new(); - } - // Match the type of the field. - match &self.parsed.ty { - syn::Type::Array(_) => quote! { - for element in self.#field.iter() { - revision::Revisioned::serialize_revisioned(element, writer)?; - } - }, - syn::Type::Path(_) => quote! { - revision::Revisioned::serialize_revisioned(&self.#field, writer)?; - }, - syn::Type::Reference(_) => quote! { - self.#field.serialize_revisioned(writer)?; - }, - v => abort!(v.span(), "Unsupported field type"), - } - } - - pub fn generate_deserializer( - &self, - current: u16, - revision: u16, - ) -> (TokenStream, TokenStream, TokenStream) { - // Get the field type. - let kind = &self.parsed.ty; - // Get the field index. - let index = syn::Index::from(self.index as usize); - // Get the field identifier. - let field = format_ident!("v{}", index); - // If the field didn't exist, use default annotation or Default trait. - if !self.exists_at(revision) { - return self.generate_deserializer_newfield(); - } - // If the field did exist, but no longer does, use convert annotation if specified. - if self.exists_at(revision) && !self.exists_at(current) { - return self.generate_deserializer_oldfield(); - } - // Output the token streams - ( - // Deserialize the field from the reader - quote! { - let #field = <#kind as revision::Revisioned>::deserialize_revisioned(reader)?; - }, - // Insert the field value into the struct - quote! { - #index: #field, - }, - // No need for any field post-processing - quote! {}, - ) - } - - fn generate_deserializer_newfield(&self) -> (TokenStream, TokenStream, TokenStream) { - let index = syn::Index::from(self.index as usize); - // Output the token streams - ( - // Field did not exist, so don't deserialize it - quote! {}, - // Set the field default value on the struct - match &self.parsed.default_fn { - Some(default_fn) => { - let default_fn = syn::Ident::new(default_fn, Span::call_site()); - quote! { - #index: Self::#default_fn(revision), - } - } - None => quote! { - #index: Default::default(), - }, - }, - // No need for any field post-processing - quote! {}, - ) - } - - fn generate_deserializer_oldfield(&self) -> (TokenStream, TokenStream, TokenStream) { - // Get the field type. - let kind = &self.parsed.ty; - // Get the field index. - let index = syn::Index::from(self.index as usize); - // Get the field identifier. - let field = format_ident!("v{}", index); - // Output the token streams - ( - // Deserialize the field which no longer exists - quote! { - let #field = <#kind as revision::Revisioned>::deserialize_revisioned(reader)?; - }, - // Don't insert the field into the current struct - quote! { - // TODO: remove this field entirely using proc macro - #index: Default::default(), - }, - // Post process the field data with the struct - match &self.parsed.convert_fn { - Some(convert_fn) => { - let convert_fn = syn::Ident::new(convert_fn, Span::call_site()); - quote! { - object.#convert_fn(revision, #field)?; - } - } - None => quote! {}, - }, - ) - } -} diff --git a/derive/src/fields/struct_inner.rs b/derive/src/fields/struct_inner.rs deleted file mode 100644 index 19b029f..0000000 --- a/derive/src/fields/struct_inner.rs +++ /dev/null @@ -1,63 +0,0 @@ -use super::struct_field::*; -use super::struct_index::*; -use crate::common::Exists; -use proc_macro2::TokenStream; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub(crate) enum StructInner { - StructField(StructField), - StructIndex(StructIndex), -} - -impl Exists for StructInner { - fn start_revision(&self) -> u16 { - match self { - Self::StructField(v) => v.start_revision(), - Self::StructIndex(v) => v.start_revision(), - } - } - fn end_revision(&self) -> Option { - match self { - Self::StructField(v) => v.end_revision(), - Self::StructIndex(v) => v.end_revision(), - } - } - fn sub_revision(&self) -> u16 { - match self { - Self::StructField(v) => v.sub_revision(), - Self::StructIndex(v) => v.sub_revision(), - } - } -} - -impl StructInner { - pub fn check_attributes(&self, current: u16) { - match self { - Self::StructField(v) => v.check_attributes(current), - Self::StructIndex(v) => v.check_attributes(current), - } - } - pub fn generate_serializer(&self, current: u16) -> TokenStream { - match self { - Self::StructField(v) => v.generate_serializer(current), - Self::StructIndex(v) => v.generate_serializer(current), - } - } - pub fn generate_deserializer( - &self, - current: u16, - revision: u16, - ) -> (TokenStream, TokenStream, TokenStream) { - match self { - Self::StructField(v) => v.generate_deserializer(current, revision), - Self::StructIndex(v) => v.generate_deserializer(current, revision), - } - } - - pub fn reexpand(&self) -> TokenStream { - match self { - Self::StructField(v) => v.reexpand(), - Self::StructIndex(v) => v.reexpand(), - } - } -} diff --git a/derive/src/helpers.rs b/derive/src/helpers.rs deleted file mode 100644 index 3d594bb..0000000 --- a/derive/src/helpers.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::common::Exists; - -/// Compute current struct revision by finding the latest field change revision. -pub(crate) fn compute_revision(fields: &[T]) -> u16 -where - T: Exists, -{ - let mut revision = 1; - for field in fields { - let beg = field.start_revision(); - let end = field.end_revision(); - let sub = field.sub_revision(); - revision = revision.max(beg).max(end.unwrap_or(0)).max(sub); - } - revision -} diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 49f7334..35fa786 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -9,28 +9,10 @@ //! chrono::DateTime, geo::Point, geo::LineString geo::Polygon, geo::MultiPoint, //! geo::MultiLineString, and geo::MultiPolygon. -mod common; -mod descriptors; -mod fields; -mod helpers; - -use common::Descriptor; -use darling::ast::NestedMeta; -use darling::{Error, FromMeta}; -use descriptors::enum_desc::EnumDescriptor; -use descriptors::struct_desc::StructDescriptor; use proc_macro::TokenStream; -use proc_macro_error::proc_macro_error; -use quote::quote; -use syn::spanned::Spanned; -use syn::{parse_macro_input, Item}; -#[derive(Debug, FromMeta)] -struct Arguments { - revision: u16, - #[allow(dead_code)] - expire: Option, -} +mod ast; +mod expand; /// Generates serialization and deserialization code as an implementation of /// the `Revisioned` trait for structs and enums. @@ -104,9 +86,10 @@ struct Arguments { /// /// The function name needs to be specified as a string. The first function /// argument is the source revision that is being deserialized, and the return -/// value is the same type as the field. +/// value is the same type as the field or an error. /// /// ```ignore +/// use revision::Error; /// use revision::revisioned; /// /// #[derive(Debug)] @@ -118,7 +101,7 @@ struct Arguments { /// } /// /// impl TestStruct { -/// fn default_b(_revision: u16) -> u8 { +/// fn default_b(_revision: u16) -> Result { /// 12u8 /// } /// } @@ -129,20 +112,32 @@ struct Arguments { /// If defined, the method is called when the field existed at some previous /// revision, but no longer exists in the latest revision. The implementation /// and behaviour is slightly different depending on whether it is applied to -/// a removed struct field or a removed enum variant. If defined, the function -/// name needs to be specified as a string, and will be called when the field -/// existed at a previous revision, but no longer exists in the latest revision. +/// a removed struct field or a removed enum variant or a removed field from an +/// enum variant. If defined, the function name needs to be specified as a +/// string, and will be called when the field existed at a previous revision, +/// but no longer exists in the latest revision. /// /// When defined on a removed struct field, the first function argument is the /// `&mut self` of the struct to update, the second argument is the source /// revision that was deserialized, and the third argument is the deserialized /// value from the field which has been removed. /// -/// When defined on a removed enum variant field, the first function argument -/// is the source revision that was deserialized, and the second argument is a -/// tuple with the enum variant field values for the variant which has been -/// removed. If the enum variant is unit-like, then an empty tuple will be used -/// for the second argument. +/// When working with an enum variant the convert function works with a fields +/// struct. This is a generated structure which has the same fields as the enum +/// variant. By default this struct is named +/// '`{enum name}{variant name}Fields`', this name can be changed with the +/// `fields_name` if desired. +/// +/// When a field in a variant is removed the convert +/// function takes a mutable reference to this fields struct as its first +/// argument, it's second argument is the revision from which this field is +/// being deserialized and it's third argument is the deserialized value. +/// +/// When the entire variant is remove the first argument is the fields +/// struct with it's fields containing the values of the deserialized removed +/// variant. In both situations the convert_fn function takes as a second +/// argument the revision from which this was serialized. The function should +/// return a result with either the right deserialized value or an error. /// /// ```ignore /// use revision::Error; @@ -166,122 +161,29 @@ struct Arguments { /// } /// /// #[derive(Debug)] -/// #[revisioned(revision = 2)] +/// #[revisioned(revision = 3)] /// enum SomeTuple { /// One, /// #[revision(end = 2, convert_fn = "convert_variant_two")] /// Two(i64, u32), /// #[revision(start = 2)] -/// Three(i64, u64, bool), +/// Three(i64, u64, #[revision(end = 3, convert_fn = "convert_variant_three_field")] bool), /// } /// /// impl SomeTuple { -/// fn convert_variant_two(_revision: u16, (a, b): (i64, u32)) -> Result { -/// Ok(Self::Three(a, b as u64, true)) +/// fn convert_variant_two(fields: SomeTupleTwoFields, _revision: u16) -> Result { +/// Ok(Self::Three(fields.a, fields.b as u64, true)) +/// } +/// +/// fn convert_variant_three_field(fields: &mut SomeTupleTwoFields, _revision: u16, v: bool) -> Result<(), Error> { +/// Ok(()) /// } /// } /// ``` #[proc_macro_attribute] -#[proc_macro_error] pub fn revisioned(attrs: TokenStream, input: TokenStream) -> proc_macro::TokenStream { - // Parse the current struct input - let input: Item = parse_macro_input!(input as Item); - - // Store the macro position - let span = input.span(); - - // Parse the current struct input - let attrs: proc_macro2::TokenStream = attrs.into(); - - let attrs_span = attrs.span(); - - // Parse the specified attributes - let attrs = match NestedMeta::parse_meta_list(attrs) { - Ok(v) => v, - Err(e) => { - return TokenStream::from(Error::from(e).write_errors()); - } - }; - - let args = match Arguments::from_list(&attrs) { - Ok(v) => v, - Err(e) => { - return TokenStream::from(e.write_errors()); - } - }; - - let (ident, generics, specified, descriptor) = match input { - Item::Enum(ref enum_) => { - let ident = enum_.ident.clone(); - let generics = enum_.generics.clone(); - let specified = args.revision; - - let descriptor: Box = Box::new(EnumDescriptor::new(enum_)); - (ident, generics, specified, descriptor) - } - Item::Struct(ref struct_) => { - let ident = struct_.ident.clone(); - let generics = struct_.generics.clone(); - let specified = args.revision; - let descriptor: Box = Box::new(StructDescriptor::new(struct_)); - (ident, generics, specified, descriptor) - } - _ => { - return syn::Error::new( - attrs_span, - "the `revisioned` attribute can only be applied to enums or structs", - ) - .into_compile_error() - .into() - } - }; - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - // - // TODO: Parse the `input` struct fields or enum variants and comment - // them out in the source code. This allows us to completely remove a - // field or variant. The subsequent derive macro can then read all - // fields or variants, along with markup comments. So for example: - // #[revision(end = 2)] - // name: String, - // could become: - // #[revision(end = 2)] - // // revision:remove name: String - // - // Extract the specified revision - - let revision = descriptor.revision(); - let serializer = descriptor.generate_serializer(); - let deserializer = descriptor.generate_deserializer(); - let item = descriptor.reexpand(); - - if specified != revision { - return syn::Error::new( - span, - format!("Expected struct revision {revision}, but found {specified}. Ensure fields are versioned correctly."), - ) - .to_compile_error() - .into(); + match expand::revision(attrs.into(), input.into()) { + Ok(x) => x.into(), + Err(e) => e.into_compile_error().into(), } - - (quote! { - #item - - #[automatically_derived] - impl #impl_generics revision::Revisioned for #ident #ty_generics #where_clause { - /// Returns the current revision of this type. - fn revision() -> u16 { - #revision - } - /// Serializes the struct using the specficifed `writer`. - fn serialize_revisioned(&self, writer: &mut W) -> std::result::Result<(), revision::Error> { - #serializer - } - /// Deserializes a new instance of the struct from the specficifed `reader`. - fn deserialize_revisioned(mut reader: &mut R) -> std::result::Result { - #deserializer - } - } - }) - .into() } diff --git a/src/implementations/string.rs b/src/implementations/string.rs index 686c510..66f2593 100644 --- a/src/implementations/string.rs +++ b/src/implementations/string.rs @@ -38,10 +38,8 @@ impl Revisioned for char { let mut buffer = [0u8; 4]; r.read_exact(&mut buffer[..1]).map_err(Error::Io)?; - dbg!(buffer[0]); - println!("{:b}", buffer[0]); + let len = CHAR_LENGTH[buffer[0] as usize]; - let len = dbg!(CHAR_LENGTH[buffer[0] as usize]); if len == 0 { return Err(Error::InvalidCharEncoding); } diff --git a/tests/test.rs b/tests/test.rs index 7be57a9..8c9e24b 100755 --- a/tests/test.rs +++ b/tests/test.rs @@ -19,49 +19,63 @@ pub enum TestEnum { a: i64, #[revision(end = 3, convert_fn = "upgrade_three_b")] b: f32, - #[revision(start = 3)] + #[revision(start = 3, default_fn = "default_three_c")] c: f64, - #[revision(start = 3)] + #[revision(start = 3, default_fn = "default_three_d")] d: String, }, - #[revision(end = 3, convert_fn = "upgrade_four")] + #[revision(end = 3, convert_fn = "upgrade_four", fields_name = "OldTestEnumFourFields")] Four, #[revision(start = 3)] Four(usize), + Five(#[revision(end = 3, convert_fn = "upgrade_five_field")] u64, #[revision(start = 3)] i64), } impl TestEnum { - fn upgrade_one(_revision: u16, (v0,): (u32,)) -> Result { - Ok(Self::Two(v0 as u64)) + fn default_three_c(_revision: u16) -> Result { + Ok(0.0) } - fn upgrade_three_b(&mut self, _revision: u16, value: f32) -> Result<(), Error> { - match self { - TestEnum::Three { - ref mut c, - .. - } => { - *c = value as f64; - } - _ => unreachable!(), - } + + fn default_three_d(_revision: u16) -> Result { + Ok("Foo".to_string()) + } + + fn upgrade_one(fields: TestEnumOneFields, _revision: u16) -> Result { + Ok(Self::Two(fields.0 as u64)) + } + fn upgrade_three_b( + fields: &mut TestEnumThreeFields, + _revision: u16, + value: f32, + ) -> Result<(), Error> { + fields.c = value as f64; Ok(()) } - fn upgrade_four(_revision: u16, _: ()) -> Result { + fn upgrade_four(_fields: OldTestEnumFourFields, _revision: u16) -> Result { Ok(TestEnum::Four(0)) } + + fn upgrade_five_field( + fields: &mut TestEnumFiveFields, + _revision: u16, + v: u64, + ) -> Result<(), Error> { + fields.0 = v as i64; + Ok(()) + } } -#[derive(Debug, Default, PartialEq)] #[revisioned(revision = 1)] +#[derive(Debug, Default, PartialEq)] pub struct TestUnit; -#[derive(Debug, Default, PartialEq)] #[revisioned(revision = 1)] +#[derive(Debug, Default, PartialEq)] pub struct TestTuple1(pub Vec); -#[derive(Debug, Default, PartialEq)] #[revisioned(revision = 2)] +#[derive(Debug, Default, PartialEq)] pub struct TestTuple2( #[revision(end = 2, convert_fn = "convert_tuple")] pub Vec, #[revision(start = 2)] pub Vec, @@ -69,14 +83,14 @@ pub struct TestTuple2( impl TestTuple2 { fn convert_tuple(&mut self, _revision: u16, old: Vec) -> Result<(), Error> { - self.1 = old.into_iter().map(|v| v as f64).collect(); + self.0 = old.into_iter().map(|v| v as f64).collect(); Ok(()) } } // Used to serialize the struct at revision 1 -#[derive(Debug, PartialEq)] #[revisioned(revision = 1)] +#[derive(Debug, PartialEq)] pub struct Tester1 { #[revision(start = 1)] // used to force the version to 1 usize_1: usize, @@ -95,8 +109,8 @@ pub struct Tester1 { } // Used to serialize the struct at revision 2 -#[derive(Debug, PartialEq)] #[revisioned(revision = 2)] +#[derive(Debug, PartialEq)] pub struct Tester2 { #[revision(start = 2)] // used to force the version to 2 usize_1: usize, @@ -119,8 +133,8 @@ pub struct Tester2 { } // Used to serialize the struct at revision 3 -#[derive(Debug, PartialEq)] #[revisioned(revision = 3)] +#[derive(Debug, PartialEq)] pub struct Tester3 { #[revision(start = 3)] // used to force the version to 3 usize_1: usize, @@ -143,8 +157,8 @@ pub struct Tester3 { wrapping_1: Wrapping, } -#[derive(Debug, PartialEq)] #[revisioned(revision = 4)] +#[derive(Debug, PartialEq)] pub struct Tester4 { usize_1: usize, #[revision(start = 2, end = 4, convert_fn = "convert_isize_1")] @@ -179,8 +193,8 @@ pub struct Tester4 { } impl Tester4 { - pub fn default_bool(_revision: u16) -> bool { - true + pub fn default_bool(_revision: u16) -> Result { + Ok(true) } pub fn convert_isize_1(&self, _revision: u16, _value: isize) -> Result<(), revision::Error> { Ok(()) @@ -197,11 +211,11 @@ impl Tester4 { pub fn convert_vec_1(&self, _revision: u16, _value: Vec) -> Result<(), revision::Error> { Ok(()) } - pub fn default_wrapping_1(_revision: u16) -> Wrapping { - Wrapping(19348719) + pub fn default_wrapping_1(_revision: u16) -> Result, revision::Error> { + Ok(Wrapping(19348719)) } - pub fn default_tuple_1(_revision: u16) -> TestTuple1 { - TestTuple1(vec![1039481, 30459830]) + pub fn default_tuple_1(_revision: u16) -> Result { + Ok(TestTuple1(vec![1039481, 30459830])) } } @@ -282,13 +296,9 @@ fn test_enum() { // Version 4 let version_4 = Tester4 { usize_1: 57918374, - isize_1: 18540294, u16_1: 19357, - u64_1: 194712409848, i8_1: 123, - i16_1: 32753, i32_1: 5283715, - i64_1: 194738194731, f32_1: 6739457.293487, f64_1: 394857394.987219847, char_1: 'x', @@ -296,7 +306,6 @@ fn test_enum() { string_1: String::from("this is a test"), enum_1: TestEnum::Zero, option_1: Some(17), - vec_1: vec!['a', 'b', 'c'], unit_1: TestUnit {}, tuple_1: TestTuple1(vec![1039481, 30459830]), box_1: Box::new(String::from("this was a test")), @@ -310,13 +319,9 @@ fn test_enum() { // Version final let version_final = Tester4 { usize_1: 57918374, - isize_1: 0, u16_1: 19357, - u64_1: 0, i8_1: 123, - i16_1: 0, i32_1: 5283715, - i64_1: 0, f32_1: 6739457.293487, f64_1: 394857394.987219847, char_1: 'x', @@ -324,7 +329,6 @@ fn test_enum() { string_1: String::from("this is a test"), enum_1: TestEnum::Zero, option_1: Some(17), - vec_1: vec![], unit_1: TestUnit {}, tuple_1: TestTuple1(vec![1039481, 30459830]), box_1: Box::new(String::from("this was a test")),