rust_libretro_sys_proc/
lib.rs

1#![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/// This macro is based on [enum-tryfrom](https://github.com/kwohlfahrt/enum-tryfrom) (MIT).
33///
34/// Original license:
35/// > MIT License
36/// >
37/// > Copyright (c) 2017 Kai Wohlfahrt
38/// >
39/// > Permission is hereby granted, free of charge, to any person obtaining a copy
40/// > of this software and associated documentation files (the "Software"), to deal
41/// > in the Software without restriction, including without limitation the rights
42/// > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
43/// > copies of the Software, and to permit persons to whom the Software is
44/// > furnished to do so, subject to the following conditions:
45/// >
46/// > The above copyright notice and this permission notice shall be included in all
47/// > copies or substantial portions of the Software.
48/// >
49/// > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
50/// > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
51/// > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
52/// > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
53/// > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
54/// > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
55/// > SOFTWARE.
56#[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}