physx_macros/
lib.rs

1#![warn(clippy::all)]
2#![warn(rust_2018_idioms)]
3#![recursion_limit = "128"]
4
5//! # 🎳 physx-macros
6//!
7//! [![Build Status](https://travis-ci.com/EmbarkStudios/physx-rs.svg?branch=master)](https://travis-ci.com/EmbarkStudios/physx-rs)
8//! [![Crates.io](https://img.shields.io/crates/v/physx-macros.svg)](https://crates.io/crates/physx-macros)
9//! [![Docs](https://docs.rs/physx/badge.svg)](https://docs.rs/physx)
10//! [![Contributor Covenant](https://img.shields.io/badge/contributor%20covenant-v1.4%20adopted-ff69b4.svg)](../CODE_OF_CONDUCT.md)
11//! [![Embark](https://img.shields.io/badge/embark-open%20source-blueviolet.svg)](http://embark.games)
12//!
13//! Utility macros used internally by the [`physx`](https://crates.io/crates/physx) crate.
14//!
15//! ## License
16//!
17//! Licensed under either of
18//!
19//! * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
20//! * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
21//!
22//! at your option.
23//!
24//! Note that the [PhysX C++ SDK](https://github.com/NVIDIAGameWorks/PhysX) has it's [own BSD 3 license](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/License.html) and depends on [additional C++ third party libraries](https://github.com/NVIDIAGameWorks/PhysX/tree/4.1/externals).
25//!
26//! ### Contribution
27//!
28//! Unless you explicitly state otherwise, any contribution intentionally
29//! submitted for inclusion in the work by you, as defined in the Apache-2.0
30//! license, shall be dual licensed as above, without any additional terms or
31//! conditions.
32
33extern 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}