summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLizzy Fleckenstein <eliasfleckenstein@web.de>2023-02-13 20:01:25 +0100
committerLizzy Fleckenstein <eliasfleckenstein@web.de>2023-02-13 20:01:25 +0100
commit2197857cd5561babfa42c744b935c9203721240e (patch)
treef7f6159842810fc4dae5eb2eb3edb7ee29cc5685
parent87e5c7b20c9832d9151a9c3fb2dd37a6e96100bf (diff)
downloadmt_ser-2197857cd5561babfa42c744b935c9203721240e.tar.xz
Support string repr
-rw-r--r--derive/.gitignore4
-rw-r--r--derive/Cargo.lock101
-rw-r--r--derive/Cargo.toml1
-rw-r--r--derive/src/lib.rs213
-rw-r--r--src/lib.rs4
5 files changed, 130 insertions, 193 deletions
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::<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");
}
});
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<dyn Debug>),
#[error("invalid constant - wanted: {0:?} - got: {1:?}")]
InvalidConst(Box<dyn Debug>, Box<dyn Debug>),
#[error("{0}")]