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 roots: BTreeMap<String, proc_macro2::TokenStream>,
18 pub plugins: Vec<Arc<dyn Plugin>>,
19}
20impl 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![], };
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 }
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 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 }
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 .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