]> git.lizzy.rs Git - mt_ser.git/commitdiff
Support string repr
authorLizzy Fleckenstein <eliasfleckenstein@web.de>
Mon, 13 Feb 2023 19:01:25 +0000 (20:01 +0100)
committerLizzy Fleckenstein <eliasfleckenstein@web.de>
Mon, 13 Feb 2023 19:01:25 +0000 (20:01 +0100)
derive/.gitignore
derive/Cargo.lock [deleted file]
derive/Cargo.toml
derive/src/lib.rs
src/lib.rs

index 4fffb2f89cbd8f2169ce9914bd16bd43785bb368..a9d37c560c6ab8d4afbf47eda643e8c42e857716 100644 (file)
@@ -1,2 +1,2 @@
-/target
-/Cargo.lock
+target
+Cargo.lock
diff --git a/derive/Cargo.lock b/derive/Cargo.lock
deleted file mode 100644 (file)
index 63bbb8c..0000000
+++ /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"
index d4a770211f17de16cf20a7f3f77a006da52c1554..e92a531b5b438338ab0cead5561e6abd84d6fc57 100644 (file)
@@ -7,6 +7,7 @@ edition = "2021"
 proc-macro = true
 
 [dependencies]
+convert_case = "0.6.0"
 darling = "0.14.2"
 proc-macro2 = "1"
 quote = "1"
index 82d964421b8628e624ebc18d2ed49ce2b86c8929..ae9842c2e249ce711d6fa68d420d1374cea94b4c 100644 (file)
@@ -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::<mt_ser::DefCfg>(&((#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::<mt_ser::DefCfg>(&((#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::<DefCfg>(__reader)?;
+                        match __discr.as_str()
+                    }
+                } else {
+                    quote! {
+                        let __discr = mt_ser::MtDeserialize::mt_deserialize::<DefCfg>(__reader)?;
+                        match __discr
+                    }
+                };
 
-            quote! {
-                #consts
+                quote! {
+                    #consts
 
-                match mt_ser::MtDeserialize::mt_deserialize::<DefCfg>(__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");
         }
     });
 
index d5d35d3223145ee1ebc99cd8d0cb790c5a49374a..7a85add8e7717ef10f7953ce79bac0447da68c13 100644 (file)
@@ -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<dyn Debug>),
     #[error("invalid constant - wanted: {0:?} - got: {1:?}")]
     InvalidConst(Box<dyn Debug>, Box<dyn Debug>),
     #[error("{0}")]