zng_env_proc_macros/
lib.rs
1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12use std::path::PathBuf;
13
14use proc_macro::TokenStream;
15use semver::Version;
16
17#[macro_use]
18extern crate quote;
19
20#[doc(hidden)]
21#[proc_macro]
22pub fn init_parse(crate_: TokenStream) -> TokenStream {
23 let crate_ = proc_macro2::TokenStream::from(crate_);
24
25 let manifest = match std::env::var("CARGO_MANIFEST_DIR") {
26 Ok(m) => PathBuf::from(m).join("Cargo.toml"),
27 Err(e) => {
28 let msg = format!("missing CARGO_MANIFEST_DIR, {e}");
29 return quote! {
30 compile_error!(#msg)
31 }
32 .into();
33 }
34 };
35 let manifest_str = match std::fs::read_to_string(&manifest) {
36 Ok(s) => s,
37 Err(e) => {
38 let msg = format!("cannot read `{}`, {e}", manifest.display());
39 return quote! {
40 compile_error!(#msg)
41 }
42 .into();
43 }
44 };
45
46 let m: Manifest = match toml::from_str(&manifest_str) {
47 Ok(m) => m,
48 Err(e) => {
49 let msg = format!("cannot parse Cargo.toml manifest, {e}");
50 return quote! {
51 compile_error!(#msg)
52 }
53 .into();
54 }
55 };
56 let p_name = m.package.name;
57 let c_name = p_name.replace('-', "_");
58 let p_authors = m.package.authors.unwrap_or_default();
59 let major = m.package.version.major;
60 let minor = m.package.version.minor;
61 let patch = m.package.version.patch;
62 let pre = m.package.version.pre.to_string();
63 let build = m.package.version.build.to_string();
64 let desc = m.package.description.unwrap_or_default();
65 let home = m.package.homepage.unwrap_or_default();
66 let license = m.package.license.unwrap_or_default();
67 let mut app = "";
68 let mut org = "";
69 let mut qualifier = "";
70 let mut has_about = false;
71
72 if let Some(m) = m
73 .package
74 .metadata
75 .as_ref()
76 .and_then(|m| m.zng.as_ref())
77 .and_then(|z| z.about.as_ref())
78 {
79 has_about = true;
80 app = m.app.as_deref().unwrap_or_default();
81 org = m.org.as_deref().unwrap_or_default();
82 qualifier = m.qualifier.as_deref().unwrap_or_default();
83 }
84 if app.is_empty() {
85 app = &p_name;
86 }
87 if org.is_empty() {
88 org = p_authors.first().map(|s| s.as_str()).unwrap_or_default();
89 }
90
91 quote! {
108 #crate_::init(#crate_::About::macro_new(
109 #p_name,
110 &[#(#p_authors),*],
111 #c_name,
112 (#major, #minor, #patch, #pre, #build),
113 #app,
114 #org,
115 #qualifier,
116 #desc,
117 #home,
118 #license,
119 #has_about,
120 ))
121 }
122 .into()
123}
124
125#[derive(serde::Deserialize)]
126struct Manifest {
127 package: Package,
128}
129#[derive(serde::Deserialize)]
130struct Package {
131 name: String,
132 version: Version,
133 description: Option<String>,
134 homepage: Option<String>,
135 license: Option<String>,
136 authors: Option<Box<[String]>>,
137 metadata: Option<Metadata>,
138}
139#[derive(serde::Deserialize)]
140struct Metadata {
141 zng: Option<Zng>,
142}
143#[derive(serde::Deserialize)]
144struct Zng {
145 about: Option<MetadataAbout>,
146}
147#[derive(serde::Deserialize)]
148struct MetadataAbout {
149 app: Option<String>,
150 org: Option<String>,
151 qualifier: Option<String>,
152}
153
154#[doc(hidden)]
155#[proc_macro]
156pub fn wasm_process_start(crate_closure: TokenStream) -> TokenStream {
158 use quote::TokenStreamExt as _;
159
160 let crate_closure = proc_macro2::TokenStream::from(crate_closure);
161 let mut crate_ = proc_macro2::TokenStream::new();
162 let mut crate_ok = false;
163 let mut closure = proc_macro2::TokenStream::new();
164
165 for tt in crate_closure {
166 if crate_ok {
167 closure.append(tt);
168 } else if matches!(&tt, proc_macro2::TokenTree::Punct(p) if p.as_char() == ',') {
169 crate_ok = true;
170 } else {
171 crate_.append(tt);
172 }
173 }
174
175 use sha2::Digest;
176 let mut start_ident = sha2::Sha256::new();
177 start_ident.update(closure.to_string().as_bytes());
178 let start_ident = format!("__zng_env_start_{:x}", start_ident.finalize());
179 let start_ident = proc_macro2::Ident::new(&start_ident, proc_macro2::Span::call_site());
180
181 quote! {
182 #[doc(hidden)]
183 #[#crate_::wasm_bindgen]
184 pub fn #start_ident() {
185 #crate_::WASM_INIT.with_borrow_mut(|v| {
186 v.push(_on_process_start);
187 })
188 }
189 fn _on_process_start(args: &#crate_::ProcessStartArgs) {
190 fn on_process_start(args: &#crate_::ProcessStartArgs, handler: impl FnOnce(&#crate_::ProcessStartArgs)) {
191 handler(args)
192 }
193 on_process_start(args, #closure)
194 }
195 }
196 .into()
197}