summaryrefslogtreecommitdiff
path: root/derive
diff options
context:
space:
mode:
Diffstat (limited to 'derive')
-rw-r--r--derive/Cargo.lock101
-rw-r--r--derive/Cargo.toml13
-rw-r--r--derive/src/lib.rs170
3 files changed, 284 insertions, 0 deletions
diff --git a/derive/Cargo.lock b/derive/Cargo.lock
new file mode 100644
index 0000000..63bbb8c
--- /dev/null
+++ b/derive/Cargo.lock
@@ -0,0 +1,101 @@
+# 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
new file mode 100644
index 0000000..5597554
--- /dev/null
+++ b/derive/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "mt_data_derive"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = { version = "1", features = ["full", "extra-traits", "printing"] }
+quote = "1"
+proc-macro2 = "1"
+darling = "0.14.2"
diff --git a/derive/src/lib.rs b/derive/src/lib.rs
new file mode 100644
index 0000000..8daabf0
--- /dev/null
+++ b/derive/src/lib.rs
@@ -0,0 +1,170 @@
+use darling::FromMeta;
+use proc_macro::{self, TokenStream};
+use quote::{quote, ToTokens};
+use syn::{parse_macro_input, parse_quote};
+
+#[derive(Debug, FromMeta, Copy, Clone, Eq, PartialEq)]
+#[darling(rename_all = "snake_case")]
+enum To {
+ Clt,
+ Srv,
+}
+
+#[derive(Debug, FromMeta)]
+struct MacroArgs {
+ to: To,
+ repr: Option<syn::Type>,
+ tag: Option<String>,
+ content: Option<String>,
+ #[darling(default)]
+ enumset: bool,
+}
+
+fn wrap_attr(attr: &mut syn::Attribute) {
+ match attr.path.get_ident().map(|i| i.to_string()).as_deref() {
+ Some("mt") => {
+ let path = attr.path.clone();
+ let tokens = attr.tokens.clone();
+
+ *attr = parse_quote! {
+ #[cfg_attr(any(feature = "client", feature = "server"), #path #tokens)]
+ };
+ }
+ Some("serde") => {
+ let path = attr.path.clone();
+ let tokens = attr.tokens.clone();
+
+ *attr = parse_quote! {
+ #[cfg_attr(feature = "serde", #path #tokens)]
+ };
+ }
+ _ => {}
+ }
+}
+
+#[proc_macro_attribute]
+pub fn mt_derive(attr: TokenStream, item: TokenStream) -> TokenStream {
+ let item2 = item.clone();
+
+ let attr_args = parse_macro_input!(attr as syn::AttributeArgs);
+ let mut input = parse_macro_input!(item2 as syn::Item);
+
+ let args = match MacroArgs::from_list(&attr_args) {
+ Ok(v) => v,
+ Err(e) => {
+ return TokenStream::from(e.write_errors());
+ }
+ };
+
+ let (serializer, deserializer) = match args.to {
+ To::Clt => ("server", "client"),
+ To::Srv => ("client", "server"),
+ };
+
+ let mut out = quote! {
+ #[derive(Debug)]
+ #[cfg_attr(feature = "random", derive(GenerateRandom))]
+ #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+ };
+
+ macro_rules! iter {
+ ($t:expr, $f:expr) => {
+ $t.iter_mut().for_each($f)
+ };
+ }
+
+ match &mut input {
+ syn::Item::Enum(e) => {
+ iter!(e.attrs, wrap_attr);
+ iter!(e.variants, |v| {
+ iter!(v.attrs, wrap_attr);
+ iter!(v.fields, |f| iter!(f.attrs, wrap_attr));
+ });
+
+ let repr = args.repr.expect("missing repr for enum");
+
+ if args.enumset {
+ let repr_str = repr.to_token_stream().to_string();
+
+ out.extend(quote! {
+ #[derive(EnumSetType)]
+ #[enumset(repr = #repr_str, serialize_as_map)]
+ })
+ } else {
+ let has_payload = e
+ .variants
+ .iter()
+ .find_map(|v| if v.fields.is_empty() { None } else { Some(()) })
+ .is_some();
+
+ if has_payload {
+ let tag = args.tag.expect("missing tag for enum with payload");
+
+ out.extend(quote! {
+ #[derive(Clone)]
+ #[cfg_attr(feature = "serde", serde(tag = #tag))]
+ });
+
+ if let Some(content) = args.content {
+ out.extend(quote! {
+ #[cfg_attr(feature = "serde", serde(content = #content))]
+ });
+ }
+ } else {
+ out.extend(quote! {
+ #[derive(Copy, Clone, PartialEq, Eq)]
+ });
+ }
+
+ out.extend(quote! {
+ #[repr(#repr)]
+ #[cfg_attr(feature = #serializer, derive(MtSerialize))]
+ #[cfg_attr(feature = #deserializer, derive(MtDeserialize))]
+ });
+ }
+
+ out.extend(quote! {
+ #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+ });
+ }
+ syn::Item::Struct(s) => {
+ iter!(s.attrs, wrap_attr);
+ iter!(s.fields, |f| iter!(f.attrs, wrap_attr));
+
+ out.extend(quote! {
+ #[derive(Clone)]
+ #[cfg_attr(feature = #serializer, derive(MtSerialize))]
+ #[cfg_attr(feature = #deserializer, derive(MtDeserialize))]
+ });
+ }
+ _ => panic!("only enum and struct supported"),
+ }
+
+ out.extend(input.to_token_stream());
+ out.into()
+}
+
+#[proc_macro_derive(MtSerialize, attributes(mt))]
+pub fn derive_serialize(input: TokenStream) -> TokenStream {
+ let syn::DeriveInput { ident, .. } = parse_macro_input!(input);
+ let output = quote! {
+ impl MtSerialize for #ident {
+ fn mt_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<(), mt_data::SerializeError> {
+ Err(mt_data::SerializeError::Unimplemented)
+ }
+ }
+ };
+ output.into()
+}
+
+#[proc_macro_derive(MtDeserialize, attributes(mt))]
+pub fn derive_deserialize(input: TokenStream) -> TokenStream {
+ let syn::DeriveInput { ident, .. } = parse_macro_input!(input);
+ quote! {
+ impl MtDeserialize for #ident {
+ fn mt_deserialize<R: std::io::Read>(reader: &mut R) -> Result<Self, mt_data::DeserializeError> {
+ Err(mt_data::DeserializeError::Unimplemented)
+ }
+ }
+ }.into()
+}