From: Lizzy Fleckenstein Date: Mon, 13 Feb 2023 19:01:25 +0000 (+0100) Subject: Support string repr X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=2197857cd5561babfa42c744b935c9203721240e;p=mt_ser.git Support string repr --- diff --git a/derive/.gitignore b/derive/.gitignore index 4fffb2f..a9d37c5 100644 --- a/derive/.gitignore +++ b/derive/.gitignore @@ -1,2 +1,2 @@ -/target -/Cargo.lock +target +Cargo.lock diff --git a/derive/Cargo.lock b/derive/Cargo.lock deleted file mode 100644 index 63bbb8c..0000000 --- a/derive/Cargo.lock +++ /dev/null @@ -1,101 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "darling" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "mt_data_derive" -version = "0.1.0" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" diff --git a/derive/Cargo.toml b/derive/Cargo.toml index d4a7702..e92a531 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" proc-macro = true [dependencies] +convert_case = "0.6.0" darling = "0.14.2" proc-macro2 = "1" quote = "1" diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 82d9644..ae9842c 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,3 +1,4 @@ +use convert_case::{Case, Casing}; use darling::{FromDeriveInput, FromField, FromMeta, FromVariant}; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokStr; @@ -117,14 +118,6 @@ pub fn mt_derive(attr: TokenStream, item: TokenStream) -> TokenStream { }); } - if let Some(repr) = args.repr { - out.extend(quote! { - #[repr(#repr)] - }); - } else if !args.custom { - panic!("missing repr for enum"); - } - out.extend(quote! { #[derive(Clone, PartialEq)] }); @@ -135,6 +128,20 @@ pub fn mt_derive(attr: TokenStream, item: TokenStream) -> TokenStream { #[cfg_attr(feature = #deserializer, derive(MtDeserialize))] }); } + + if let Some(repr) = args.repr { + if repr == parse_quote! { str } { + out.extend(quote! { + #[mt(string_repr)] + }); + } else { + out.extend(quote! { + #[repr(#repr)] + }); + } + } else if !args.custom { + panic!("missing repr for enum"); + } } out.extend(quote! { @@ -180,6 +187,8 @@ struct MtArgs { zlib: bool, zstd: bool, // TODO default: bool, // type must implement Default + + string_repr: bool, // for enums } type Fields<'a> = Vec<(TokStr, &'a syn::Field)>; @@ -392,14 +401,35 @@ fn get_fields_struct(input: &syn::Fields) -> (Fields, TokStr) { (fields, fields_struct) } -fn get_repr(input: &syn::DeriveInput) -> syn::Type { - input - .attrs - .iter() - .find(|a| a.path.is_ident("repr")) - .expect("missing repr") - .parse_args() - .expect("invalid repr") +fn get_repr(input: &syn::DeriveInput, args: &MtArgs) -> syn::Type { + if args.string_repr { + parse_quote! { &str } + } else { + input + .attrs + .iter() + .find(|a| a.path.is_ident("repr")) + .expect("missing repr") + .parse_args() + .expect("invalid repr") + } +} + +fn iter_variants(e: &syn::DataEnum, args: &MtArgs, mut f: impl FnMut(&syn::Variant, &syn::Expr)) { + let mut discr = parse_quote! { 0 }; + + for v in e.variants.iter() { + discr = if args.string_repr { + let lit = v.ident.to_string().to_case(Case::Snake); + parse_quote! { #lit } + } else { + v.discriminant.clone().map(|x| x.1).unwrap_or(discr) + }; + + f(&v, &discr); + + discr = parse_quote! { 1 + #discr }; + } } #[proc_macro_derive(MtSerialize, attributes(mt))] @@ -407,40 +437,38 @@ pub fn derive_serialize(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as syn::DeriveInput); let typename = &input.ident; - let code = serialize_args(MtArgs::from_derive_input(&input), |_| match &input.data { - syn::Data::Enum(e) => { - let repr = get_repr(&input); - let variants: TokStr = e.variants - .iter() - .fold((parse_quote! { 0 }, TokStr::new()), |(discr, before), v| { - let discr = v.discriminant.clone().map(|x| x.1).unwrap_or(discr); - let (fields, fields_struct) = get_fields_struct(&v.fields); - - let code = serialize_args(MtArgs::from_variant(v), |_| - serialize_fields(&fields)); - let variant = &v.ident; - - ( - parse_quote! { 1 + #discr }, - quote! { - #before - #typename::#variant #fields_struct => { - mt_ser::MtSerialize::mt_serialize::(&((#discr) as #repr), __writer)?; - #code - } - } - ) - }).1; + let code = serialize_args(MtArgs::from_derive_input(&input), |args| { + match &input.data { + syn::Data::Enum(e) => { + let repr = get_repr(&input, &args); + let mut variants = TokStr::new(); + + iter_variants(&e, &args, |v, discr| { + let (fields, fields_struct) = get_fields_struct(&v.fields); + let code = + serialize_args(MtArgs::from_variant(v), |_| serialize_fields(&fields)); + let ident = &v.ident; + + variants.extend(quote! { + #typename::#ident #fields_struct => { + mt_ser::MtSerialize::mt_serialize::(&((#discr) as #repr), __writer)?; + #code + } + }); + }); - quote! { - match self { - #variants + quote! { + match self { + #variants + } } } - } - syn::Data::Struct(s) => serialize_fields(&get_fields(&s.fields, |f| quote! { &self.#f })), - _ => { - panic!("only enum and struct supported"); + syn::Data::Struct(s) => { + serialize_fields(&get_fields(&s.fields, |f| quote! { &self.#f })) + } + _ => { + panic!("only enum and struct supported"); + } } }); @@ -461,60 +489,69 @@ pub fn derive_deserialize(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as syn::DeriveInput); let typename = &input.ident; - let code = deserialize_args(MtArgs::from_derive_input(&input), |_| match &input.data { - syn::Data::Enum(e) => { - let repr = get_repr(&input); - let type_str = typename.to_string(); + let code = deserialize_args(MtArgs::from_derive_input(&input), |args| { + match &input.data { + syn::Data::Enum(e) => { + let repr = get_repr(&input, &args); - let mut consts = TokStr::new(); - let mut arms = TokStr::new(); - let mut discr = parse_quote! { 0 }; + let mut consts = TokStr::new(); + let mut arms = TokStr::new(); - for v in e.variants.iter() { - discr = v.discriminant.clone().map(|x| x.1).unwrap_or(discr); + iter_variants(&e, &args, |v, discr| { + let ident = &v.ident; + let (fields, fields_struct) = get_fields_struct(&v.fields); + let code = deserialize_args(MtArgs::from_variant(v), |_| { + let fields_code = deserialize_fields(&fields); - let ident = &v.ident; - let (fields, fields_struct) = get_fields_struct(&v.fields); - let code = deserialize_args(MtArgs::from_variant(v), |_| { - let fields_code = deserialize_fields(&fields); - - quote! { - #fields_code - Ok(Self::#ident #fields_struct) - } - }); + quote! { + #fields_code + Ok(Self::#ident #fields_struct) + } + }); - consts.extend(quote! { - const #ident: #repr = #discr; - }); + consts.extend(quote! { + const #ident: #repr = #discr; + }); - arms.extend(quote! { - #ident => { #code } + arms.extend(quote! { + #ident => { #code } + }); }); - discr = parse_quote! { 1 + #discr }; - } + let type_str = typename.to_string(); + let discr_match = if args.string_repr { + quote! { + let __discr = String::mt_deserialize::(__reader)?; + match __discr.as_str() + } + } else { + quote! { + let __discr = mt_ser::MtDeserialize::mt_deserialize::(__reader)?; + match __discr + } + }; - quote! { - #consts + quote! { + #consts - match mt_ser::MtDeserialize::mt_deserialize::(__reader)? { - #arms - x => Err(mt_ser::DeserializeError::InvalidEnumVariant(#type_str, x as u64)) + #discr_match { + #arms + _ => Err(mt_ser::DeserializeError::InvalidEnum(#type_str, Box::new(__discr))) + } } } - } - syn::Data::Struct(s) => { - let (fields, fields_struct) = get_fields_struct(&s.fields); - let code = deserialize_fields(&fields); + syn::Data::Struct(s) => { + let (fields, fields_struct) = get_fields_struct(&s.fields); + let code = deserialize_fields(&fields); - quote! { - #code - Ok(Self #fields_struct) + quote! { + #code + Ok(Self #fields_struct) + } + } + _ => { + panic!("only enum and struct supported"); } - } - _ => { - panic!("only enum and struct supported"); } }); diff --git a/src/lib.rs b/src/lib.rs index d5d35d3..7a85add 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,8 +48,8 @@ pub enum DeserializeError { TooBig(#[from] TryFromIntError), #[error("invalid UTF-16: {0}")] InvalidUtf16(#[from] std::char::DecodeUtf16Error), - #[error("invalid {0} enum variant {1}")] - InvalidEnumVariant(&'static str, u64), + #[error("invalid {0} enum variant {1:?}")] + InvalidEnum(&'static str, Box), #[error("invalid constant - wanted: {0:?} - got: {1:?}")] InvalidConst(Box, Box), #[error("{0}")]