1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#![warn(clippy::all)]
#![warn(rust_2018_idioms)]
#![recursion_limit = "128"]

//! # 🎳 physx-macros
//!
//! [![Build Status](https://travis-ci.com/EmbarkStudios/physx-rs.svg?branch=master)](https://travis-ci.com/EmbarkStudios/physx-rs)
//! [![Crates.io](https://img.shields.io/crates/v/physx-macros.svg)](https://crates.io/crates/physx-macros)
//! [![Docs](https://docs.rs/physx/badge.svg)](https://docs.rs/physx)
//! [![Contributor Covenant](https://img.shields.io/badge/contributor%20covenant-v1.4%20adopted-ff69b4.svg)](../CODE_OF_CONDUCT.md)
//! [![Embark](https://img.shields.io/badge/embark-open%20source-blueviolet.svg)](http://embark.games)
//!
//! Utility macros used internally by the [`physx`](https://crates.io/crates/physx) crate.
//!
//! ## License
//!
//! Licensed under either of
//!
//! * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
//! * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
//!
//! at your option.
//!
//! 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).
//!
//! ### Contribution
//!
//! Unless you explicitly state otherwise, any contribution intentionally
//! submitted for inclusion in the work by you, as defined in the Apache-2.0
//! license, shall be dual licensed as above, without any additional terms or
//! conditions.

extern crate proc_macro;

use proc_macro2::{Ident, Span, TokenStream, TokenTree};
use quote::quote;
use syn::{parse_macro_input, AttributeArgs, Lit, Meta, NestedMeta};

#[proc_macro_attribute]
pub fn physx_type(
    attr: proc_macro::TokenStream,
    item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let item = proc_macro2::TokenStream::from(item);
    let attr = MacroArgs::from_meta(parse_macro_input!(attr as AttributeArgs));
    let itm = item.clone();
    let info = ParseTarget::parse(itm);
    let name = info.name;
    let pxname = Ident::new(&(String::from("Px") + &name.to_string()), Span::call_site());

    let output1 = quote! {
        pub type #name = PxType<#pxname>;
        impl PxPtr for #pxname {}

    };

    let ts = if let Some(inherit) = attr.inherit {
        let parent = Ident::new(&inherit, Span::call_site());
        quote! {
        use core::ops::{Deref, DerefMut};
        impl Deref for #name {
            type Target = #parent;
            fn deref(&self) -> &Self::Target {
                self.as_ptr()
            }
        }

        impl DerefMut for #name {
            fn deref_mut(&mut self) -> &mut Self::Target {
                self.as_ptr_mut()
            }
        }
        }
    } else {
        TokenStream::new()
    };

    let output: TokenStream = output1
        .into_iter()
        .chain(ts.into_iter())
        .chain(item.into_iter())
        .collect();

    proc_macro::TokenStream::from(output)
}

#[derive(Debug)]
struct ParseTarget {
    name: Ident,
}

impl ParseTarget {
    fn parse(stream: TokenStream) -> Self {
        let mut iter = stream.into_iter();
        let _struct_token = iter.next();
        let name = iter.next();
        let name = if let Some(f) = name {
            if let TokenTree::Ident(f) = f {
                Some(f)
            } else {
                None
            }
        } else {
            None
        };

        ParseTarget {
            name: name.unwrap(),
        }
    }
}
#[derive(Default, Debug)]
struct MacroArgs {
    inherit: Option<String>,
}

impl MacroArgs {
    fn from_meta(args: AttributeArgs) -> Self {
        let mut margs = Self::default();

        for item in args.iter() {
            match item {
                NestedMeta::Meta(meta) => {
                    if let Meta::NameValue(namevalue) = meta {
                        match meta.path().get_ident() {
                            Some(ident) if ident == "inherit" => {
                                margs.inherit = visit_literal_to_string(&namevalue.lit)
                            }
                            _ => (),
                        }
                    }
                }
                NestedMeta::Lit(_) => panic!("Expected ident but found literal"),
            }
        }
        margs
    }
}

fn visit_literal_to_string(lit: &Lit) -> Option<String> {
    match lit {
        Lit::Str(val) => Some(val.value()),
        _ => None,
    }
}