wars_macro/
lib.rs

1use std::{collections::BTreeMap, sync::Arc};
2
3use expander::{Edition, Expander};
4use proc_macro::TokenStream;
5use proc_macro2::Span;
6use quote::quote;
7use syn::{parse::Parse, parse_macro_input, Ident, LitBool, Path, Token};
8use wars::{Flags, Opts, Plugin};
9
10struct O {
11    pub crate_path: syn::Path,
12    pub module: Vec<u8>,
13    pub name: Ident,
14    pub flags: Flags,
15    pub embed: proc_macro2::TokenStream,
16    pub data: BTreeMap<Ident, proc_macro2::TokenStream>, // pub cfg: Arc<dyn ImportCfg>,
17    pub roots: BTreeMap<String, proc_macro2::TokenStream>,
18    pub plugins: Vec<Arc<dyn Plugin>>,
19}
20// struct NoopCfg {}
21// impl ImportCfg for NoopCfg {
22//     fn import(&self, module: &str, name: &str) -> proc_macro2::TokenStream {
23//         quote! {
24//             compile_error!("import used")
25//         }
26//     }
27// }
28impl Parse for O {
29    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
30        let mut o = O {
31            crate_path: syn::parse(quote! {::wars_rt}.into())?,
32            module: vec![],
33            name: Ident::new("Wars", Span::call_site()),
34            flags: Default::default(),
35            embed: Default::default(),
36            data: BTreeMap::new(),
37            roots: BTreeMap::new(),
38            plugins: vec![], // cfg: Arc::new(NoopCfg {}),
39        };
40        while input.lookahead1().peek(Ident) {
41            let i: Ident = input.parse()?;
42            let _eq: Token![=] = input.parse()?;
43            match i.to_string().as_str() {
44                "crate_path" => {
45                    let s: syn::LitStr = input.parse()?;
46                    o.crate_path = s.parse()?;
47                }
48                "env" => {
49                    let s: syn::LitStr = input.parse()?;
50                    let sp = s.span();
51                    let s = std::env::var(s.value()).map_err(|x| syn::Error::new(sp, x))?;
52                    let x = std::fs::read(s).map_err(|x| syn::Error::new(sp, x))?;
53                    o.module = x;
54                }
55                "file" => {
56                    let s: syn::LitStr = input.parse()?;
57                    let sp = s.span();
58                    let x = std::fs::read(s.value()).map_err(|x| syn::Error::new(sp, x))?;
59                    o.module = x;
60                }
61                "inline" => {
62                    let s: syn::LitStr = input.parse()?;
63                    let sp = s.span();
64                    let x = wat::parse_str(&s.value()).map_err(|x| syn::Error::new(sp, x))?;
65                    o.module = x;
66                }
67                "name" => {
68                    o.name = input.parse()?;
69                }
70                "r#async" => {
71                    let b: LitBool = input.parse()?;
72                    if b.value {
73                        o.flags |= Flags::ASYNC
74                    } else {
75                        o.flags &= Flags::ASYNC.complement();
76                    }
77                }
78                "legacy_host" => {
79                    let b: LitBool = input.parse()?;
80                    if b.value {
81                        o.flags |= Flags::LEGACY
82                    } else {
83                        o.flags &= Flags::LEGACY.complement();
84                    }
85                }
86                "lpit" => {
87                    let b: LitBool = input.parse()?;
88                    if b.value {
89                        o.plugins
90                            .push(Arc::new(wars_pit_plugin::PitPlugin::default()));
91                    } else {
92                        // o.flags &= Flags::PIT.complement();
93                    }
94                }
95                _ => {
96                    match i
97                        .to_string()
98                        .as_str()
99                        .strip_suffix("_path")
100                        .map(|a| a.to_owned())
101                    {
102                        None => return Err(syn::Error::new(i.span(), "unexpected type")),
103                        Some(a) => {
104                            let s: syn::LitStr = input.parse()?;
105                            o.roots.insert(a, s.parse()?);
106                        }
107                    }
108                }
109            };
110            let _comma: Token![,] = input.parse()?;
111        }
112        // while input.lookahead1().peek(Token![;]){
113        //     let _semi: Token![;] = input.parse()?;
114        // }
115        Ok(o)
116    }
117}
118
119#[proc_macro]
120pub fn wars(a: TokenStream) -> TokenStream {
121    let o = parse_macro_input!(a as O);
122    let x = wars::go(
123        &Opts {
124            crate_path: o.crate_path,
125            module: o.module,
126            name: o.name,
127            flags: o.flags,
128            embed: o.embed,
129            data: o.data,
130            roots: o.roots,
131            plugins: o.plugins,
132            // tpit: Default::default(),
133            // cfg: o.cfg,
134        }
135        .to_mod(),
136    );
137    let x = match x {
138        Ok(a) => a,
139        Err(e) => {
140            return syn::Error::new(Span::call_site(), e)
141                .into_compile_error()
142                .into()
143        }
144    };
145    let expanded = Expander::new("wars")
146        .add_comment("This is generated code!".to_owned())
147        .fmt(Edition::_2021)
148        .verbose(true)
149        // common way of gating this, by making it part of the default feature set
150        .dry(cfg!(feature = "no-file-expansion"))
151        .write_to_out_dir(x.clone())
152        .unwrap_or_else(|e| {
153            eprintln!("Failed to write to file: {:?}", e);
154            x
155        });
156    expanded.into()
157}
158
159// #[proc_macro]
160// pub fn wasix(a: TokenStream) -> TokenStream {
161//     let x = wars::wasix::wasix();
162//     let expanded = Expander::new("wars_wasix")
163//         .add_comment("This is generated code!".to_owned())
164//         .fmt(Edition::_2021)
165//         .verbose(true)
166//         // common way of gating this, by making it part of the default feature set
167//         .dry(cfg!(feature = "no-file-expansion"))
168//         .write_to_out_dir(x.clone())
169//         .unwrap_or_else(|e| {
170//             eprintln!("Failed to write to file: {:?}", e);
171//             x
172//         });
173//     expanded.into()
174// }