cosmian_wit_bindgen_rust_impl/
lib.rs

1use cosmian_wit_bindgen_gen_core::{cosmian_wit_parser::Interface, Direction, Files, Generator};
2use proc_macro::TokenStream;
3use syn::parse::{Error, Parse, ParseStream, Result};
4use syn::punctuated::Punctuated;
5use syn::{token, Token};
6
7#[proc_macro]
8pub fn import(input: TokenStream) -> TokenStream {
9    run(input, Direction::Import)
10}
11
12#[proc_macro]
13pub fn export(input: TokenStream) -> TokenStream {
14    run(input, Direction::Export)
15}
16
17fn run(input: TokenStream, dir: Direction) -> TokenStream {
18    let input = syn::parse_macro_input!(input as Opts);
19    let mut gen = input.opts.build();
20    let mut files = Files::default();
21    let (imports, exports) = match dir {
22        Direction::Import => (input.interfaces, vec![]),
23        Direction::Export => (vec![], input.interfaces),
24    };
25    gen.generate_all(&imports, &exports, &mut files);
26    let (_, contents) = files.iter().next().unwrap();
27    let mut contents = std::str::from_utf8(contents).unwrap().to_string();
28
29    // Include a dummy `include_str!` for any files we read so rustc knows that
30    // we depend on the contents of those files.
31    let cwd = std::env::current_dir().unwrap();
32    for file in input.files.iter() {
33        contents.push_str(&format!(
34            "const _: &str = include_str!(r#\"{}\"#);\n",
35            cwd.join(file).display()
36        ));
37    }
38
39    contents.parse().unwrap()
40}
41
42struct Opts {
43    opts: cosmian_wit_bindgen_gen_rust_wasm::Opts,
44    interfaces: Vec<Interface>,
45    files: Vec<String>,
46}
47
48mod kw {
49    syn::custom_keyword!(src);
50    syn::custom_keyword!(paths);
51    syn::custom_keyword!(unchecked);
52    syn::custom_keyword!(multi_module);
53}
54
55impl Parse for Opts {
56    fn parse(input: ParseStream<'_>) -> Result<Opts> {
57        let mut opts = cosmian_wit_bindgen_gen_rust_wasm::Opts::default();
58        let call_site = proc_macro2::Span::call_site();
59        let mut files = Vec::new();
60        let interfaces = if input.peek(token::Brace) {
61            let content;
62            syn::braced!(content in input);
63            let mut interfaces = Vec::new();
64            let fields = Punctuated::<ConfigField, Token![,]>::parse_terminated(&content)?;
65            for field in fields.into_pairs() {
66                match field.into_value() {
67                    ConfigField::Unchecked => opts.unchecked = true,
68                    ConfigField::MultiModule => opts.multi_module = true,
69                    ConfigField::Interfaces(v) => interfaces = v,
70                }
71            }
72            if interfaces.is_empty() {
73                return Err(Error::new(
74                    call_site,
75                    "must either specify `src` or `paths` keys",
76                ));
77            }
78            interfaces
79        } else {
80            while !input.is_empty() {
81                let s = input.parse::<syn::LitStr>()?;
82                files.push(s.value());
83            }
84            let mut interfaces = Vec::new();
85            for path in files.iter() {
86                let iface = Interface::parse_file(path).map_err(|e| Error::new(call_site, e))?;
87                interfaces.push(iface);
88            }
89            interfaces
90        };
91        Ok(Opts {
92            files,
93            opts,
94            interfaces,
95        })
96    }
97}
98
99enum ConfigField {
100    Interfaces(Vec<Interface>),
101    Unchecked,
102    MultiModule,
103}
104
105impl Parse for ConfigField {
106    fn parse(input: ParseStream<'_>) -> Result<Self> {
107        let l = input.lookahead1();
108        if l.peek(kw::src) {
109            input.parse::<kw::src>()?;
110            let name;
111            syn::bracketed!(name in input);
112            let name = name.parse::<syn::LitStr>()?;
113            input.parse::<Token![:]>()?;
114            let s = input.parse::<syn::LitStr>()?;
115            let interface =
116                Interface::parse(&name.value(), &s.value()).map_err(|e| Error::new(s.span(), e))?;
117            Ok(ConfigField::Interfaces(vec![interface]))
118        } else if l.peek(kw::paths) {
119            input.parse::<kw::paths>()?;
120            input.parse::<Token![:]>()?;
121            let paths;
122            let bracket = syn::bracketed!(paths in input);
123            let paths = Punctuated::<syn::LitStr, Token![,]>::parse_terminated(&paths)?;
124            let values = paths.iter().map(|s| s.value()).collect::<Vec<_>>();
125            let mut interfaces = Vec::new();
126            for value in &values {
127                let interface =
128                    Interface::parse_file(value).map_err(|e| Error::new(bracket.span, e))?;
129                interfaces.push(interface);
130            }
131            Ok(ConfigField::Interfaces(interfaces))
132        } else if l.peek(kw::unchecked) {
133            input.parse::<kw::unchecked>()?;
134            Ok(ConfigField::Unchecked)
135        } else if l.peek(kw::multi_module) {
136            input.parse::<kw::multi_module>()?;
137            Ok(ConfigField::MultiModule)
138        } else {
139            Err(l.error())
140        }
141    }
142}