mvutils_proc_macro/
lib.rs1extern crate proc_macro;
2
3use crate::savable::{enumerator, named, unit, unnamed};
4use proc_macro::TokenStream;
5use std::str::FromStr;
6use quote::quote;
7use syn::{parse_macro_input, Data, DeriveInput, Fields, Meta};
8
9mod savable;
10
11#[proc_macro_derive(Savable, attributes(unsaved, custom, varint))]
12pub fn derive_savable(input: TokenStream) -> TokenStream {
13 let input = parse_macro_input!(input as DeriveInput);
14
15 let name = input.ident;
16 let generics = input.generics;
17
18 let varint = input.attrs.iter().any(|attr| {
19 if let Meta::Path(ref p) = attr.meta {
20 p.segments.iter().any(|s| s.ident == "varint")
21 } else {
22 false
23 }
24 });
25
26 match &input.data {
27 Data::Struct(s) => match &s.fields {
28 Fields::Named(fields) => named(fields, name, generics),
29 Fields::Unnamed(fields) => unnamed(fields, name, generics),
30 Fields::Unit => unit(name, generics),
31 },
32 Data::Enum(e) => enumerator(e, name, generics, varint),
33 Data::Union(_) => panic!("Deriving Savable for unions is not supported!"),
34 }
35}
36
37#[proc_macro_derive(TryFromString, attributes(exclude))]
38pub fn try_from_string(input: TokenStream) -> TokenStream {
39 let mut input = parse_macro_input!(input as DeriveInput);
40 let name = input.ident.clone();
41
42 fn is_excluded(v: &&syn::Variant) -> bool {
43 v.attrs.iter().any(|attr| {
44 if let Meta::Path(ref p) = attr.meta {
45 p.segments.iter().any(|s| s.ident == "exclude")
46 } else {
47 false
48 }
49 })
50 }
51
52 match &input.data {
53 Data::Enum(e) => {
54 let values = e.variants.iter().filter(|v| !is_excluded(v)).map(|v| {
55 let str = v.ident.to_string();
56 let alt = str.chars().next().unwrap().to_lowercase().to_string() + &str.chars().skip(1).map(|c| {
57 if c.is_uppercase() {
58 "_".to_string() + &c.to_lowercase().to_string()
59 } else {
60 c.to_string()
61 }
62 }).collect::<String>();
63 format!("\"{}\" => Ok(Self::{}),\n\"{}\" => Ok(Self::{}),", str, str, alt, str)
64 }).map(|s| {
65 proc_macro2::TokenStream::from_str(&s).unwrap()
66 });
67 quote! {
68 impl core::str::FromStr for #name {
69 type Err = ();
70
71 fn from_str(value: &str) -> Result<Self, Self::Err> {
72 match value {
73 #( #values )*
74 _ => Err(())
75 }
76 }
77 }
78 }.into()
79 },
80 _ => panic!("`try_from_string` is only meant for enums")
81 }
82}