dynamic_object_derive/
lib.rs

1//! derive for crate dynamic_object
2#![allow(non_snake_case)]
3
4use proc_macro::{TokenStream};
5use syn::{*, parse::Parse, punctuated::Punctuated};
6use std::time::UNIX_EPOCH;
7use quote::quote;
8use uuid;
9
10extern crate proc_macro;
11
12struct Parent {
13      parent: Type,
14}
15
16impl Parse for Parent {
17      fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
18            let vars = Punctuated::<Type, Token![,]>::parse_terminated(input)?;
19            let parent = match vars.first() {
20                  Some(p) => p,
21                  None => {
22                        panic!("Error: #[subclass] takes a parent(ParentT, parent_field)");
23                  },
24            };
25
26            Ok(Self {
27                  parent: parent.clone(),
28            })
29      }
30}
31
32struct ParentFieldName {
33      name: Option<Ident>
34}
35
36impl Parse for ParentFieldName {
37      fn parse(input: parse::ParseStream) -> Result<Self> {
38            let vars = Punctuated::<Ident, Token![,]>::parse_terminated(input)?;
39            let vars: Vec<_> = vars.iter().collect();
40
41            let name = if vars.len() > 1 {
42                  Some(vars[1].clone())
43            } else {
44                  None
45            };
46            Ok(Self {
47                  name
48            })
49      }
50}
51
52fn generateID(name: &Ident) -> String {
53      let now = std::time::SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
54      let id = uuid::Uuid::new_v4();
55      format!("{:?}{}{}", now, id, name.to_string())
56}
57
58fn offsetof(parent: &Type, parentField: ParentFieldName) -> __private::TokenStream2 {
59      match parentField.name {
60            Some(name) => {
61                  return quote! {
62                        fn offset() -> isize {
63                              let this: *const Self = 0 as *const Self;
64                              let parent = unsafe {
65                                    &((*this).#name) as *const _ as *const u8
66                              };
67                              parent as isize
68                        }
69                  }
70            },
71            None => {}
72      }
73      match *parent {
74            Type::Path(ref path) => {
75                  match path.path.segments.iter().last() {
76                        Some(parent) => {
77                              // TODO: resolve fully qualified name of DynamicObjectBase
78                              // Allow base class to omit parent field
79                              if parent.ident.to_string() == "DynamicObjectBase" {
80                                    return quote! {
81                                          fn offset() -> isize {
82                                                0
83                                          }
84                                    }
85                              }
86                        },
87                        None => {}
88                  }
89            },
90            _ => panic!("Expected super class")
91      }
92      panic!("Expect parent field name in second argument of attribute");
93}
94
95#[proc_macro_attribute]
96pub fn subclass(args: TokenStream, tokens: TokenStream) -> TokenStream {
97      let argsCpy = args.clone();
98      let parse = parse_macro_input!(tokens as ItemStruct);
99      let parent = parse_macro_input!(args as Parent).parent;
100      let name = &parse.ident;
101
102      let parentField = parse_macro_input!(argsCpy as ParentFieldName);
103      let id = generateID(name);
104      let offsetof = offsetof(&parent, parentField);
105
106      let result = quote! {
107            #parse
108
109            impl dynamic_object::Class for #name {
110                  type Parent = #parent;
111                  const NAME:&'static str = #id;
112
113                  fn isa(id: usize) -> bool {
114                        id == Self::id() || <Self as dynamic_object::Class>::Parent::isa(id)
115                  }
116
117                  // fn offsetof() -> usize;
118                  #offsetof
119
120                  fn id() -> usize {
121                        Self::id as *const u8 as usize
122                  }
123            }
124      };
125      TokenStream::from(result)
126}
127
128#[proc_macro_attribute]
129pub fn module_name(_: TokenStream, stream: TokenStream) -> TokenStream {
130      let parse = parse_macro_input!(stream as ItemStruct);
131
132      let name = &parse.ident;
133
134      let result = quote! {
135            #parse
136
137            impl #name {
138
139            }
140      };
141      TokenStream::from(result)
142}