havok_types_derive_proc_macros/
mod.rs
1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::{ToTokens, quote};
5use syn::{Ident, Token, braced, parse::Parse, punctuated::Punctuated, token::PathSep};
6
7#[proc_macro_attribute]
16pub fn impl_flags_methods(_attr: TokenStream, input: TokenStream) -> TokenStream {
17 let bit_flags = syn::parse_macro_input!(input as BitFlagsMacro);
18 let struct_ = &bit_flags.struct_;
19 let struct_type = struct_.ty.to_token_stream().to_string();
20 let struct_ident = &struct_.ident;
21 let struct_name_str = struct_.ident.to_string();
22
23 let mut str_push_matchers = Vec::new();
24 let mut matchers = Vec::new();
25 let mut field_names = Vec::new();
26 for field in &struct_.fields {
27 let field_ident = &field.name;
28 let flag_str = field.name.to_string();
29
30 str_push_matchers.push(quote! {
31 #struct_ident::#field_ident => flags.push(#flag_str.into()),
32 });
33
34 matchers.push(quote! {
35 s if s.eq_ignore_ascii_case(#flag_str) => flags |= #struct_ident::#field_ident,
36 });
37
38 field_names.push(flag_str);
39 }
40
41 let err_msg = format!(
42 "Invalid {struct_name_str}({struct_type}): {{err}} '{{unknown}}'. Expected one or more values separated by '|', or custom bits. Valid values are: {}.",
43 field_names.join(", ")
44 );
45 quote! {
49 #bit_flags
50
51 impl Default for #struct_ident {
52 #[inline]
53 fn default() -> Self {
54 Self::empty()
55 }
56 }
57
58 impl core::fmt::Display for #struct_ident {
59 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
60 if self.is_empty() {
61 return write!(f, "0");
62 }
63
64 let mut flags: Vec<std::borrow::Cow<'_, str>> = Vec::new();
65 for flag in self.iter() {
66 match flag {
67 #(#str_push_matchers)*
68 remain => flags.push(format!("{:#X}", remain.bits() as u32).into()),
69 };
70 }
71
72 write!(f, "{}", flags.join("|"))
73 }
74 }
75
76 impl core::str::FromStr for #struct_ident {
78 type Err = String;
79
80 fn from_str(s: &str) -> Result<Self, Self::Err> {
81 if s == "0" {
82 return Ok(#struct_ident::empty());
83 }
84
85 let mut flags = #struct_ident::empty();
86 for token in s.split('|') {
87 let token = token.trim();
88
89 let token = if let Some(start) = token.find("<!--") {
92 if let Some(end) = token[start..].find("-->") {
93 let before_comment = token[..start].trim();
94 let after_comment = token[(start + end + 3)..].trim();
95 if !before_comment.is_empty() {
96 before_comment
97 } else {
98 after_comment
99 }
100 } else {
101 token[..start].trim() }
103 } else {
104 token
105 };
106
107 if token.is_empty() {
108 continue;
109 }
110
111 match token {
112 #(#matchers)*
113 unknown => {
114 let bits = havok_types::parse_int::ParseNumber::parse_wrapping(unknown).map_err(|err| {
115 let name = #struct_name_str;
116 format!(#err_msg)
117 })?;
118 flags |= #struct_ident::from_bits_retain(bits);
119 }
120 }
121 }
122
123 Ok(flags)
124 }
125 }
126 }
127 .into()
128}
129
130#[derive(Debug, Clone, PartialEq, Eq)]
132struct BitFlagsMacro {
133 crate_name: Ident,
134 path_seq_token: PathSep,
135 macro_name: Ident,
136 exclamation_token: Token![!],
137 brace_token: syn::token::Brace,
138 struct_: Struct,
139}
140
141impl Parse for BitFlagsMacro {
142 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
143 let crate_name = input.parse::<syn::Ident>()?;
145 let path_seq_token = input.parse::<syn::Token![::]>()?;
146 let macro_name = input.parse::<syn::Ident>()?;
147 let exclamation_token = input.parse::<syn::Token![!]>()?;
148 let content;
149 let brace_token: syn::token::Brace = braced!(content in input);
150
151 Ok(Self {
152 crate_name,
153 path_seq_token,
154 macro_name,
155 exclamation_token,
156 brace_token,
157 struct_: Struct::parse(&content)?,
158 })
159 }
160}
161
162impl quote::ToTokens for BitFlagsMacro {
164 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
165 self.crate_name.to_tokens(tokens);
166 self.path_seq_token.to_tokens(tokens);
167 self.macro_name.to_tokens(tokens);
168 self.exclamation_token.to_tokens(tokens);
169 self.brace_token
170 .surround(tokens, |tokens| self.struct_.to_tokens(tokens));
171 }
172}
173
174#[derive(Debug, Clone, PartialEq, Eq)]
177struct Struct {
178 attrs: Vec<syn::Attribute>,
179 vis: syn::Visibility,
180 struct_token: syn::Token![struct],
181 ident: syn::Ident,
182 colon_token: syn::Token![:],
183 ty: syn::Type,
184 brace_token: syn::token::Brace,
185 fields: Punctuated<Field, Token![;]>,
186}
187
188impl Parse for Struct {
189 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
190 let content;
193 let attrs: Vec<syn::Attribute> = input.call(syn::Attribute::parse_outer)?;
194 let vis: syn::Visibility = input.parse()?;
195 let struct_token = input.parse::<syn::Token![struct]>()?;
196 let ident = input.parse::<syn::Ident>()?;
197 let colon_token = input.parse::<syn::Token![:]>()?;
198 let ty = input.parse::<syn::Type>()?;
199 let brace_token: syn::token::Brace = braced!(content in input);
200
201 Ok(Self {
202 attrs,
203 vis,
204 struct_token,
205 ident,
206 colon_token,
207 ty,
208 brace_token,
209 fields: content.parse_terminated(Field::parse, Token![;])?,
210 })
211 }
212}
213
214impl ToTokens for Struct {
216 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
217 for attr in &self.attrs {
218 attr.to_tokens(tokens);
219 }
220 self.vis.to_tokens(tokens);
221 self.struct_token.to_tokens(tokens);
222 self.ident.to_tokens(tokens);
223 self.colon_token.to_tokens(tokens);
224 self.ty.to_tokens(tokens);
225 self.brace_token
226 .surround(tokens, |tokens| self.fields.to_tokens(tokens))
227 }
228}
229
230#[derive(Debug, Clone, PartialEq, Eq)]
233struct Field {
234 attrs: Vec<syn::Attribute>,
235 const_token: Token![const],
236 eq_token: Token![=],
237 name: Ident,
238 value: syn::Expr,
239}
240
241impl Parse for Field {
242 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
243 let attrs: Vec<syn::Attribute> = input.call(syn::Attribute::parse_outer)?;
244 let const_token = input.parse::<syn::Token![const]>()?;
245 let name = input.parse::<syn::Ident>()?;
246 let eq_token = input.parse::<syn::Token![=]>()?;
247 let value: syn::Expr = input.parse()?;
248
249 Ok(Field {
250 attrs,
251 const_token,
252 eq_token,
253 name,
254 value,
255 })
256 }
257}
258
259impl ToTokens for Field {
261 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
262 for attr in &self.attrs {
263 attr.to_tokens(tokens);
264 }
265 self.const_token.to_tokens(tokens);
266 self.name.to_tokens(tokens);
267 self.eq_token.to_tokens(tokens);
268 self.value.to_tokens(tokens);
269 }
270}