rust_libretro_sys_proc/
lib.rs1#![doc(
2 html_logo_url = "https://raw.githubusercontent.com/max-m/rust-libretro/master/media/logo.png",
3 html_favicon_url = "https://raw.githubusercontent.com/max-m/rust-libretro/master/media/favicon.png"
4)]
5
6use proc_macro::TokenStream;
7use proc_macro2::Span;
8use quote::quote;
9use syn::{DataEnum, DeriveInput, Meta, Meta::List, MetaList, NestedMeta};
10
11fn filter_primitive_type_attr(attr: &syn::Attribute) -> Option<(String, Span)> {
12 if let Ok(List(MetaList {
13 path,
14 paren_token: _,
15 nested,
16 })) = attr.parse_meta()
17 {
18 if let Some(ident) = path.get_ident() {
19 if ident == "repr" {
20 if let Some(NestedMeta::Meta(Meta::Path(path))) = nested.first() {
21 if let Some(ident) = path.get_ident() {
22 return Some((ident.to_string(), ident.span()));
23 }
24 }
25 }
26 }
27 }
28
29 None
30}
31
32#[proc_macro_derive(TryFromPrimitive)]
57pub fn from_primitive(input: TokenStream) -> TokenStream {
58 let input: DeriveInput = syn::parse(input).unwrap();
59
60 let name = &input.ident;
61
62 let types = input
63 .attrs
64 .iter()
65 .filter_map(filter_primitive_type_attr)
66 .map(|(ident, span)| syn::Ident::new(&ident, span));
67
68 let variants = if let syn::Data::Enum(DataEnum { ref variants, .. }) = input.data {
69 variants
70 } else {
71 panic!("`TryFromPrimitive` is only supported on Enums")
72 };
73
74 let impls = types.map(|ty| {
75 let blocks = variants.iter().map(|var| {
76 let ident = &var.ident;
77 if !matches!(var.fields, syn::Fields::Unit) {
78 panic!("Enum variant may not store data!")
79 }
80
81 quote! {
82 x if x == #name::#ident as #ty => Ok(#name::#ident)
83 }
84 });
85
86 let repr_ident = format!("{}_REPR_TYPE", name);
87 let repr_ident = syn::Ident::new(&repr_ident, name.span());
88
89 let tokens = quote! {
90 pub type #repr_ident = #ty;
91
92 impl TryFrom<#ty> for #name {
93 type Error = crate::InvalidEnumValue<#ty>;
94
95 fn try_from(v: #ty) -> Result<Self, Self::Error> {
96 match v {
97 #(#blocks,)*
98 v => Err(Self::Error::new(v))
99 }
100 }
101 }
102 };
103
104 tokens
105 });
106
107 quote! {
108 #(#impls)*
109 }
110 .into()
111}