1#![warn(clippy::all)]
2#![warn(rust_2018_idioms)]
3#![recursion_limit = "128"]
4
5extern crate proc_macro;
34
35use proc_macro2::{Ident, Span, TokenStream, TokenTree};
36use quote::quote;
37use syn::{parse_macro_input, AttributeArgs, Lit, Meta, NestedMeta};
38
39#[proc_macro_attribute]
40pub fn physx_type(
41 attr: proc_macro::TokenStream,
42 item: proc_macro::TokenStream,
43) -> proc_macro::TokenStream {
44 let item = proc_macro2::TokenStream::from(item);
45 let attr = MacroArgs::from_meta(parse_macro_input!(attr as AttributeArgs));
46 let itm = item.clone();
47 let info = ParseTarget::parse(itm);
48 let name = info.name;
49 let pxname = Ident::new(&(String::from("Px") + &name.to_string()), Span::call_site());
50
51 let output1 = quote! {
52 pub type #name = PxType<#pxname>;
53 impl PxPtr for #pxname {}
54
55 };
56
57 let ts = if let Some(inherit) = attr.inherit {
58 let parent = Ident::new(&inherit, Span::call_site());
59 quote! {
60 use core::ops::{Deref, DerefMut};
61 impl Deref for #name {
62 type Target = #parent;
63 fn deref(&self) -> &Self::Target {
64 self.as_ptr()
65 }
66 }
67
68 impl DerefMut for #name {
69 fn deref_mut(&mut self) -> &mut Self::Target {
70 self.as_ptr_mut()
71 }
72 }
73 }
74 } else {
75 TokenStream::new()
76 };
77
78 let output: TokenStream = output1
79 .into_iter()
80 .chain(ts.into_iter())
81 .chain(item.into_iter())
82 .collect();
83
84 proc_macro::TokenStream::from(output)
85}
86
87#[derive(Debug)]
88struct ParseTarget {
89 name: Ident,
90}
91
92impl ParseTarget {
93 fn parse(stream: TokenStream) -> Self {
94 let mut iter = stream.into_iter();
95 let _struct_token = iter.next();
96 let name = iter.next();
97 let name = if let Some(f) = name {
98 if let TokenTree::Ident(f) = f {
99 Some(f)
100 } else {
101 None
102 }
103 } else {
104 None
105 };
106
107 ParseTarget {
108 name: name.unwrap(),
109 }
110 }
111}
112#[derive(Default, Debug)]
113struct MacroArgs {
114 inherit: Option<String>,
115}
116
117impl MacroArgs {
118 fn from_meta(args: AttributeArgs) -> Self {
119 let mut margs = Self::default();
120
121 for item in args.iter() {
122 match item {
123 NestedMeta::Meta(meta) => {
124 if let Meta::NameValue(namevalue) = meta {
125 match meta.path().get_ident() {
126 Some(ident) if ident == "inherit" => {
127 margs.inherit = visit_literal_to_string(&namevalue.lit)
128 }
129 _ => (),
130 }
131 }
132 }
133 NestedMeta::Lit(_) => panic!("Expected ident but found literal"),
134 }
135 }
136 margs
137 }
138}
139
140fn visit_literal_to_string(lit: &Lit) -> Option<String> {
141 match lit {
142 Lit::Str(val) => Some(val.value()),
143 _ => None,
144 }
145}