blob: 70c88fc7a6ae9e7ade9e834922c699d3326a5b14 [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 {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700271 crate::UnsafePolicy::AllFunctionsUnsafe => Box::new(std::iter::empty()),
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700272 _ => Box::new(std::iter::once(policy.to_token_stream())),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700273 }
274 }
275}
276
277fn allowlist_err_to_syn_err(err: AllowlistErr, span: &Span) -> syn::Error {
278 syn::Error::new(*span, format!("{}", err))
279}
280
281struct StringList<SET, GET>(SET, GET)
282where
283 SET: Fn(&mut IncludeCppConfig) -> &mut Vec<String>,
284 GET: Fn(&IncludeCppConfig) -> &Vec<String>;
285
286impl<SET, GET> Directive for StringList<SET, GET>
287where
288 SET: Fn(&mut IncludeCppConfig) -> &mut Vec<String> + Sync + Send,
289 GET: Fn(&IncludeCppConfig) -> &Vec<String> + Sync + Send,
290{
291 fn parse(
292 &self,
293 args: ParseStream,
294 config: &mut IncludeCppConfig,
295 _ident_span: &Span,
296 ) -> ParseResult<()> {
297 let val: syn::LitStr = args.parse()?;
298 self.0(config).push(val.value());
299 Ok(())
300 }
301
302 fn output<'a>(
303 &self,
304 config: &'a IncludeCppConfig,
305 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
306 Box::new(self.1(config).iter().map(|val| {
307 quote! {
308 #val
309 }
310 }))
311 }
312}
313
314struct BoolFlag<SET, GET>(SET, GET)
315where
316 SET: Fn(&mut IncludeCppConfig) -> &mut bool,
317 GET: Fn(&IncludeCppConfig) -> &bool;
318
319impl<SET, GET> Directive for BoolFlag<SET, GET>
320where
321 SET: Fn(&mut IncludeCppConfig) -> &mut bool + Sync + Send,
322 GET: Fn(&IncludeCppConfig) -> &bool + Sync + Send,
323{
324 fn parse(
325 &self,
326 _args: ParseStream,
327 config: &mut IncludeCppConfig,
328 _ident_span: &Span,
329 ) -> ParseResult<()> {
330 *self.0(config) = true;
331 Ok(())
332 }
333
334 fn output<'a>(
335 &self,
336 config: &'a IncludeCppConfig,
337 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
338 if *self.1(config) {
339 Box::new(std::iter::once(quote! {}))
340 } else {
341 Box::new(std::iter::empty())
342 }
343 }
344}
345
346struct ModName;
347
348impl Directive for ModName {
349 fn parse(
350 &self,
351 args: ParseStream,
352 config: &mut IncludeCppConfig,
353 _ident_span: &Span,
354 ) -> ParseResult<()> {
355 let id: Ident = args.parse()?;
356 config.mod_name = Some(id);
357 Ok(())
358 }
359
360 fn output<'a>(
361 &self,
362 config: &'a IncludeCppConfig,
363 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
364 match &config.mod_name {
365 None => Box::new(std::iter::empty()),
366 Some(id) => Box::new(std::iter::once(quote! { #id })),
367 }
368 }
369}
370
371struct Concrete;
372
373impl Directive for Concrete {
374 fn parse(
375 &self,
376 args: ParseStream,
377 config: &mut IncludeCppConfig,
378 _ident_span: &Span,
379 ) -> ParseResult<()> {
380 let definition: syn::LitStr = args.parse()?;
381 args.parse::<syn::token::Comma>()?;
382 let rust_id: syn::Ident = args.parse()?;
383 config.concretes.0.insert(definition.value(), rust_id);
384 Ok(())
385 }
386
387 fn output<'a>(
388 &self,
389 config: &'a IncludeCppConfig,
390 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
391 Box::new(config.concretes.0.iter().map(|(k, v)| {
392 quote! {
393 #k,#v
394 }
395 }))
396 }
397}
398
399struct RustType {
400 output: bool,
401}
402
403impl Directive for RustType {
404 fn parse(
405 &self,
406 args: ParseStream,
407 config: &mut IncludeCppConfig,
408 _ident_span: &Span,
409 ) -> ParseResult<()> {
410 let id: Ident = args.parse()?;
411 config.rust_types.push(RustPath::new_from_ident(id));
412 Ok(())
413 }
414
415 fn output<'a>(
416 &self,
417 config: &'a IncludeCppConfig,
418 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
419 if self.output {
420 Box::new(config.rust_types.iter().map(|rp| rp.to_token_stream()))
421 } else {
422 Box::new(std::iter::empty())
423 }
424 }
425}
426
427struct Subclass;
428
429impl Directive for Subclass {
430 fn parse(
431 &self,
432 args: ParseStream,
433 config: &mut IncludeCppConfig,
434 _ident_span: &Span,
435 ) -> ParseResult<()> {
436 let superclass: syn::LitStr = args.parse()?;
437 args.parse::<syn::token::Comma>()?;
438 let subclass: syn::Ident = args.parse()?;
439 config.subclasses.push(crate::config::Subclass {
440 superclass: superclass.value(),
441 subclass,
442 });
443 Ok(())
444 }
445
446 fn output<'a>(
447 &self,
448 config: &'a IncludeCppConfig,
449 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
450 Box::new(config.subclasses.iter().map(|sc| {
451 let superclass = &sc.superclass;
452 let subclass = &sc.subclass;
453 quote! {
454 #superclass,#subclass
455 }
456 }))
457 }
458}
459
460struct ExternRustFun;
461
462impl Directive for ExternRustFun {
463 fn parse(
464 &self,
465 args: ParseStream,
466 config: &mut IncludeCppConfig,
467 _ident_span: &Span,
468 ) -> ParseResult<()> {
469 let path: RustPath = args.parse()?;
470 args.parse::<syn::token::Comma>()?;
471 let sig: syn::Signature = args.parse()?;
472 config.extern_rust_funs.push(RustFun {
473 path,
474 sig,
475 receiver: None,
476 });
477 Ok(())
478 }
479
480 fn output<'a>(
481 &self,
482 config: &'a IncludeCppConfig,
483 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
484 Box::new(config.extern_rust_funs.iter().map(|erf| {
485 let p = &erf.path;
486 let s = &erf.sig;
487 quote! { #p,#s }
488 }))
489 }
490}
491
492struct ExternCppType {
493 opaque: bool,
494}
495
496impl Directive for ExternCppType {
497 fn parse(
498 &self,
499 args: ParseStream,
500 config: &mut IncludeCppConfig,
501 _ident_span: &Span,
502 ) -> ParseResult<()> {
503 let definition: syn::LitStr = args.parse()?;
504 args.parse::<syn::token::Comma>()?;
505 let rust_path: syn::TypePath = args.parse()?;
506 config.externs.0.insert(
507 definition.value(),
508 crate::config::ExternCppType {
509 rust_path,
510 opaque: self.opaque,
511 },
512 );
513 Ok(())
514 }
515
516 fn output<'a>(
517 &self,
518 config: &'a IncludeCppConfig,
519 ) -> Box<dyn Iterator<Item = TokenStream> + 'a> {
520 let opaque_needed = self.opaque;
521 Box::new(
522 config
523 .externs
524 .0
525 .iter()
526 .filter_map(move |(definition, details)| {
527 if details.opaque == opaque_needed {
528 let rust_path = &details.rust_path;
529 Some(quote! {
530 #definition, #rust_path
531 })
532 } else {
533 None
534 }
535 }),
536 )
537 }
538}