blob: 160829ec206cfe4a969f6b18e4d4b44d021e1658 [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use indexmap::map::IndexMap as HashMap;
10
11use once_cell::sync::OnceCell;
12use proc_macro2::Span;
13use proc_macro2::{Ident, TokenStream};
14use quote::{quote, ToTokens};
15use syn::parse::ParseStream;
16
17use crate::config::{Allowlist, AllowlistErr};
18use crate::directive_names::{EXTERN_RUST_FUN, EXTERN_RUST_TYPE, SUBCLASS};
19use crate::{AllowlistEntry, IncludeCppConfig};
20use crate::{ParseResult, RustFun, RustPath};
21
22pub(crate) struct DirectivesMap {
23 pub(crate) need_hexathorpe: HashMap<String, Box<dyn Directive>>,
24 pub(crate) need_exclamation: HashMap<String, Box<dyn Directive>>,
25}
26
27static DIRECTIVES: OnceCell<DirectivesMap> = OnceCell::new();
28
29pub(crate) fn get_directives() -> &'static DirectivesMap {
30 DIRECTIVES.get_or_init(|| {
31 let mut need_hexathorpe: HashMap<String, Box<dyn Directive>> = HashMap::new();
32 need_hexathorpe.insert("include".into(), Box::new(Inclusion));
33 let mut need_exclamation: HashMap<String, Box<dyn Directive>> = HashMap::new();
34 need_exclamation.insert("generate".into(), Box::new(Generate(false)));
35 need_exclamation.insert("generate_pod".into(), Box::new(Generate(true)));
36 need_exclamation.insert("generate_ns".into(), Box::new(GenerateNs));
37 need_exclamation.insert("generate_all".into(), Box::new(GenerateAll));
38 need_exclamation.insert("safety".into(), Box::new(Safety));
39 need_exclamation.insert(
40 "pod".into(),
41 Box::new(StringList(
42 |config| &mut config.pod_requests,
43 |config| &config.pod_requests,
44 )),
45 );
46 need_exclamation.insert(
47 "block".into(),
48 Box::new(StringList(
49 |config| &mut config.blocklist,
50 |config| &config.blocklist,
51 )),
52 );
53 need_exclamation.insert(
54 "block_constructors".into(),
55 Box::new(StringList(
56 |config| &mut config.constructor_blocklist,
57 |config| &config.constructor_blocklist,
58 )),
59 );
60 need_exclamation.insert(
61 "instantiable".into(),
62 Box::new(StringList(
63 |config| &mut config.instantiable,
64 |config| &config.instantiable,
65 )),
66 );
67 need_exclamation.insert(
68 "parse_only".into(),
69 Box::new(BoolFlag(
70 |config| &mut config.parse_only,
71 |config| &config.parse_only,
72 )),
73 );
74 need_exclamation.insert(
75 "exclude_impls".into(),
76 Box::new(BoolFlag(
77 |config| &mut config.exclude_impls,
78 |config| &config.exclude_impls,
79 )),
80 );
81 need_exclamation.insert(
82 "exclude_utilities".into(),
83 Box::new(BoolFlag(
84 |config| &mut config.exclude_utilities,
85 |config| &config.exclude_utilities,
86 )),
87 );
88 need_exclamation.insert("name".into(), Box::new(ModName));
89 need_exclamation.insert("concrete".into(), Box::new(Concrete));
90 need_exclamation.insert("rust_type".into(), Box::new(RustType { output: false }));
91 need_exclamation.insert(EXTERN_RUST_TYPE.into(), Box::new(RustType { output: true }));
92 need_exclamation.insert(SUBCLASS.into(), Box::new(Subclass));
93 need_exclamation.insert(EXTERN_RUST_FUN.into(), Box::new(ExternRustFun));
94 need_exclamation.insert(
95 "extern_cpp_type".into(),
96 Box::new(ExternCppType { opaque: false }),
97 );
98 need_exclamation.insert(
99 "extern_cpp_opaque_type".into(),
100 Box::new(ExternCppType { opaque: true }),
101 );
102
103 DirectivesMap {
104 need_hexathorpe,
105 need_exclamation,
106 }
107 })
108}
109
110/// Trait for handling an `include_cpp!` configuration directive.
111pub(crate) trait Directive: Send + Sync {
112 fn parse(
113 &self,
114 args: ParseStream,
115 config: &mut IncludeCppConfig,
116 span: &Span,
117 ) -> ParseResult<()>;
118 fn output<'a>(
119 &self,
120 config: &'a IncludeCppConfig,
121 ) -> Box<dyn Iterator<Item = TokenStream> + 'a>;
122}
123
124struct Inclusion;
125
126impl Directive for Inclusion {
127 fn parse(
128 &self,
129 args: ParseStream,
130 config: &mut IncludeCppConfig,
131 _span: &Span,
132 ) -> ParseResult<()> {
133 let hdr: syn::LitStr = args.parse()?;
134 config.inclusions.push(hdr.value());
135 Ok(())
136 }
137
138 fn output<'a>(
139 &self,
140 config: &'a IncludeCppConfig,
141 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
142 Box::new(config.inclusions.iter().map(|val| quote! { #val }))
143 }
144}
145
146/// Directive for either `generate!` (false) or `generate_pod!` (true).
147struct Generate(bool);
148
149impl Directive for Generate {
150 fn parse(
151 &self,
152 args: ParseStream,
153 config: &mut IncludeCppConfig,
154 span: &Span,
155 ) -> ParseResult<()> {
156 let generate: syn::LitStr = args.parse()?;
157 config
158 .allowlist
159 .push(AllowlistEntry::Item(generate.value()))
160 .map_err(|e| allowlist_err_to_syn_err(e, span))?;
161 if self.0 {
162 config.pod_requests.push(generate.value());
163 }
164 Ok(())
165 }
166
167 fn output<'a>(
168 &self,
169 config: &'a IncludeCppConfig,
170 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
171 match &config.allowlist {
172 Allowlist::Specific(items) if !self.0 => Box::new(
173 items
174 .iter()
175 .flat_map(|i| match i {
176 AllowlistEntry::Item(s) => Some(s),
177 _ => None,
178 })
179 .map(|s| quote! { #s }),
180 ),
181 Allowlist::Unspecified(_) => panic!("Allowlist mode not yet determined"),
182 _ => Box::new(std::iter::empty()),
183 }
184 }
185}
186
187struct GenerateNs;
188
189impl Directive for GenerateNs {
190 fn parse(
191 &self,
192 args: ParseStream,
193 config: &mut IncludeCppConfig,
194 span: &Span,
195 ) -> ParseResult<()> {
196 let generate: syn::LitStr = args.parse()?;
197 config
198 .allowlist
199 .push(AllowlistEntry::Namespace(generate.value()))
200 .map_err(|e| allowlist_err_to_syn_err(e, span))?;
201 Ok(())
202 }
203
204 fn output<'a>(
205 &self,
206 config: &'a IncludeCppConfig,
207 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
208 match &config.allowlist {
209 Allowlist::Specific(items) => Box::new(
210 items
211 .iter()
212 .flat_map(|i| match i {
213 AllowlistEntry::Namespace(s) => Some(s),
214 _ => None,
215 })
216 .map(|s| quote! { #s }),
217 ),
218 Allowlist::Unspecified(_) => panic!("Allowlist mode not yet determined"),
219 _ => Box::new(std::iter::empty()),
220 }
221 }
222}
223
224struct GenerateAll;
225
226impl Directive for GenerateAll {
227 fn parse(
228 &self,
229 _args: ParseStream,
230 config: &mut IncludeCppConfig,
231 span: &Span,
232 ) -> ParseResult<()> {
233 config
234 .allowlist
235 .set_all()
236 .map_err(|e| allowlist_err_to_syn_err(e, span))?;
237 Ok(())
238 }
239
240 fn output<'a>(
241 &self,
242 config: &'a IncludeCppConfig,
243 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
244 match &config.allowlist {
245 Allowlist::All => Box::new(std::iter::once(TokenStream::new())),
246 Allowlist::Unspecified(_) => panic!("Allowlist mode not yet determined"),
247 _ => Box::new(std::iter::empty()),
248 }
249 }
250}
251
252struct Safety;
253
254impl Directive for Safety {
255 fn parse(
256 &self,
257 args: ParseStream,
258 config: &mut IncludeCppConfig,
259 _ident_span: &Span,
260 ) -> ParseResult<()> {
261 config.unsafe_policy = args.parse()?;
262 Ok(())
263 }
264
265 fn output<'a>(
266 &self,
267 config: &'a IncludeCppConfig,
268 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
269 let policy = &config.unsafe_policy;
270 match config.unsafe_policy {
271 crate::UnsafePolicy::AllFunctionsSafe => {
272 Box::new(std::iter::once(policy.to_token_stream()))
273 }
274 crate::UnsafePolicy::AllFunctionsUnsafe => Box::new(std::iter::empty()),
275 }
276 }
277}
278
279fn allowlist_err_to_syn_err(err: AllowlistErr, span: &Span) -> syn::Error {
280 syn::Error::new(*span, format!("{}", err))
281}
282
283struct StringList<SET, GET>(SET, GET)
284where
285 SET: Fn(&mut IncludeCppConfig) -> &mut Vec<String>,
286 GET: Fn(&IncludeCppConfig) -> &Vec<String>;
287
288impl<SET, GET> Directive for StringList<SET, GET>
289where
290 SET: Fn(&mut IncludeCppConfig) -> &mut Vec<String> + Sync + Send,
291 GET: Fn(&IncludeCppConfig) -> &Vec<String> + Sync + Send,
292{
293 fn parse(
294 &self,
295 args: ParseStream,
296 config: &mut IncludeCppConfig,
297 _ident_span: &Span,
298 ) -> ParseResult<()> {
299 let val: syn::LitStr = args.parse()?;
300 self.0(config).push(val.value());
301 Ok(())
302 }
303
304 fn output<'a>(
305 &self,
306 config: &'a IncludeCppConfig,
307 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
308 Box::new(self.1(config).iter().map(|val| {
309 quote! {
310 #val
311 }
312 }))
313 }
314}
315
316struct BoolFlag<SET, GET>(SET, GET)
317where
318 SET: Fn(&mut IncludeCppConfig) -> &mut bool,
319 GET: Fn(&IncludeCppConfig) -> &bool;
320
321impl<SET, GET> Directive for BoolFlag<SET, GET>
322where
323 SET: Fn(&mut IncludeCppConfig) -> &mut bool + Sync + Send,
324 GET: Fn(&IncludeCppConfig) -> &bool + Sync + Send,
325{
326 fn parse(
327 &self,
328 _args: ParseStream,
329 config: &mut IncludeCppConfig,
330 _ident_span: &Span,
331 ) -> ParseResult<()> {
332 *self.0(config) = true;
333 Ok(())
334 }
335
336 fn output<'a>(
337 &self,
338 config: &'a IncludeCppConfig,
339 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
340 if *self.1(config) {
341 Box::new(std::iter::once(quote! {}))
342 } else {
343 Box::new(std::iter::empty())
344 }
345 }
346}
347
348struct ModName;
349
350impl Directive for ModName {
351 fn parse(
352 &self,
353 args: ParseStream,
354 config: &mut IncludeCppConfig,
355 _ident_span: &Span,
356 ) -> ParseResult<()> {
357 let id: Ident = args.parse()?;
358 config.mod_name = Some(id);
359 Ok(())
360 }
361
362 fn output<'a>(
363 &self,
364 config: &'a IncludeCppConfig,
365 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
366 match &config.mod_name {
367 None => Box::new(std::iter::empty()),
368 Some(id) => Box::new(std::iter::once(quote! { #id })),
369 }
370 }
371}
372
373struct Concrete;
374
375impl Directive for Concrete {
376 fn parse(
377 &self,
378 args: ParseStream,
379 config: &mut IncludeCppConfig,
380 _ident_span: &Span,
381 ) -> ParseResult<()> {
382 let definition: syn::LitStr = args.parse()?;
383 args.parse::<syn::token::Comma>()?;
384 let rust_id: syn::Ident = args.parse()?;
385 config.concretes.0.insert(definition.value(), rust_id);
386 Ok(())
387 }
388
389 fn output<'a>(
390 &self,
391 config: &'a IncludeCppConfig,
392 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
393 Box::new(config.concretes.0.iter().map(|(k, v)| {
394 quote! {
395 #k,#v
396 }
397 }))
398 }
399}
400
401struct RustType {
402 output: bool,
403}
404
405impl Directive for RustType {
406 fn parse(
407 &self,
408 args: ParseStream,
409 config: &mut IncludeCppConfig,
410 _ident_span: &Span,
411 ) -> ParseResult<()> {
412 let id: Ident = args.parse()?;
413 config.rust_types.push(RustPath::new_from_ident(id));
414 Ok(())
415 }
416
417 fn output<'a>(
418 &self,
419 config: &'a IncludeCppConfig,
420 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
421 if self.output {
422 Box::new(config.rust_types.iter().map(|rp| rp.to_token_stream()))
423 } else {
424 Box::new(std::iter::empty())
425 }
426 }
427}
428
429struct Subclass;
430
431impl Directive for Subclass {
432 fn parse(
433 &self,
434 args: ParseStream,
435 config: &mut IncludeCppConfig,
436 _ident_span: &Span,
437 ) -> ParseResult<()> {
438 let superclass: syn::LitStr = args.parse()?;
439 args.parse::<syn::token::Comma>()?;
440 let subclass: syn::Ident = args.parse()?;
441 config.subclasses.push(crate::config::Subclass {
442 superclass: superclass.value(),
443 subclass,
444 });
445 Ok(())
446 }
447
448 fn output<'a>(
449 &self,
450 config: &'a IncludeCppConfig,
451 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
452 Box::new(config.subclasses.iter().map(|sc| {
453 let superclass = &sc.superclass;
454 let subclass = &sc.subclass;
455 quote! {
456 #superclass,#subclass
457 }
458 }))
459 }
460}
461
462struct ExternRustFun;
463
464impl Directive for ExternRustFun {
465 fn parse(
466 &self,
467 args: ParseStream,
468 config: &mut IncludeCppConfig,
469 _ident_span: &Span,
470 ) -> ParseResult<()> {
471 let path: RustPath = args.parse()?;
472 args.parse::<syn::token::Comma>()?;
473 let sig: syn::Signature = args.parse()?;
474 config.extern_rust_funs.push(RustFun {
475 path,
476 sig,
477 receiver: None,
478 });
479 Ok(())
480 }
481
482 fn output<'a>(
483 &self,
484 config: &'a IncludeCppConfig,
485 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
486 Box::new(config.extern_rust_funs.iter().map(|erf| {
487 let p = &erf.path;
488 let s = &erf.sig;
489 quote! { #p,#s }
490 }))
491 }
492}
493
494struct ExternCppType {
495 opaque: bool,
496}
497
498impl Directive for ExternCppType {
499 fn parse(
500 &self,
501 args: ParseStream,
502 config: &mut IncludeCppConfig,
503 _ident_span: &Span,
504 ) -> ParseResult<()> {
505 let definition: syn::LitStr = args.parse()?;
506 args.parse::<syn::token::Comma>()?;
507 let rust_path: syn::TypePath = args.parse()?;
508 config.externs.0.insert(
509 definition.value(),
510 crate::config::ExternCppType {
511 rust_path,
512 opaque: self.opaque,
513 },
514 );
515 Ok(())
516 }
517
518 fn output<'a>(
519 &self,
520 config: &'a IncludeCppConfig,
521 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
522 let opaque_needed = self.opaque;
523 Box::new(
524 config
525 .externs
526 .0
527 .iter()
528 .filter_map(move |(definition, details)| {
529 if details.opaque == opaque_needed {
530 let rust_path = &details.rust_path;
531 Some(quote! {
532 #definition, #rust_path
533 })
534 } else {
535 None
536 }
537 }),
538 )
539 }
540}