use crate::*;
pub(crate) const LAZY_UNIT_PREFIX: &str = "<lazy>";
pub(crate) fn is_lazy_container_new(path: &str) -> bool {
let Some(t2) = tail2(path) else { return false };
let mut it = t2.split("::");
let ty = it.next().unwrap_or("");
let m = it.next().unwrap_or("");
m == "new" && matches!(ty, "Lazy" | "LazyLock" | "LazyCell")
}
pub(crate) fn lazy_init_body(call: &syn::ExprCall) -> Option<Vec<syn::Stmt>> {
if !matches!(&*call.func, syn::Expr::Path(p) if is_lazy_container_new(&path_to_string(&p.path))) {
return None;
}
let first = call.args.first()?;
closure_or_block_stmts(first)
}
pub(crate) fn closure_or_block_stmts(e: &syn::Expr) -> Option<Vec<syn::Stmt>> {
match e {
syn::Expr::Closure(cl) => match &*cl.body {
syn::Expr::Block(b) => Some(b.block.stmts.clone()),
other => Some(vec![syn::Stmt::Expr(other.clone(), None)]),
},
syn::Expr::Block(b) => Some(b.block.stmts.clone()),
syn::Expr::Paren(p) => closure_or_block_stmts(&p.expr),
_ => None,
}
}
pub(crate) fn lazy_static_unit(it: &syn::Item) -> Option<(String, Vec<syn::Stmt>)> {
match it {
syn::Item::Static(s) => {
let syn::Expr::Call(call) = &*s.expr else { return None };
let body = lazy_init_body(call)?;
Some((s.ident.to_string(), body))
}
syn::Item::Const(c) => {
let syn::Expr::Call(call) = &*c.expr else { return None };
let body = lazy_init_body(call)?;
Some((c.ident.to_string(), body))
}
syn::Item::Macro(m) => {
let mname = path_to_string(&m.mac.path);
let mname = mname.rsplit("::").next().unwrap_or(&mname);
match mname {
"lazy_static" => lazy_static_macro_body(&m.mac.tokens),
"thread_local" => thread_local_macro_body(&m.mac.tokens),
_ => None,
}
}
_ => None,
}
}
pub(crate) fn lazy_static_macro_body(tokens: &proc_macro2::TokenStream) -> Option<(String, Vec<syn::Stmt>)> {
syn::parse2::<LazyStaticDecl>(tokens.clone())
.ok()
.map(|d| (d.name, vec![syn::Stmt::Expr(d.init, None)]))
}
pub(crate) fn thread_local_macro_body(tokens: &proc_macro2::TokenStream) -> Option<(String, Vec<syn::Stmt>)> {
syn::parse2::<ThreadLocalDecl>(tokens.clone())
.ok()
.map(|d| (d.name, vec![syn::Stmt::Expr(d.init, None)]))
}
pub(crate) struct LazyStaticDecl {
pub(crate) name: String,
pub(crate) init: syn::Expr,
}
impl syn::parse::Parse for LazyStaticDecl {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let _attrs = input.call(syn::Attribute::parse_outer)?;
let _vis: syn::Visibility = input.parse()?;
let _static: syn::Token![static] = input.parse()?;
if input.peek(syn::Token![ref]) {
let _ref: syn::Token![ref] = input.parse()?;
}
let name: syn::Ident = input.parse()?;
let _colon: syn::Token![:] = input.parse()?;
let _ty: syn::Type = input.parse()?;
let _eq: syn::Token![=] = input.parse()?;
let init: syn::Expr = input.parse()?;
let _ = input.parse::<syn::Token![;]>();
input.parse::<proc_macro2::TokenStream>()?;
Ok(LazyStaticDecl { name: name.to_string(), init })
}
}
pub(crate) struct ThreadLocalDecl {
pub(crate) name: String,
pub(crate) init: syn::Expr,
}
impl syn::parse::Parse for ThreadLocalDecl {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let _attrs = input.call(syn::Attribute::parse_outer)?;
let _vis: syn::Visibility = input.parse()?;
let _static: syn::Token![static] = input.parse()?;
let name: syn::Ident = input.parse()?;
let _colon: syn::Token![:] = input.parse()?;
let _ty: syn::Type = input.parse()?;
let _eq: syn::Token![=] = input.parse()?;
let init: syn::Expr = input.parse()?;
let _ = input.parse::<syn::Token![;]>();
input.parse::<proc_macro2::TokenStream>()?;
Ok(ThreadLocalDecl { name: name.to_string(), init })
}
}