unconst 0.13.0

Macro to unconst const/fn/enum/struct/trait/impl/type items
Documentation
mod auto_clone;
mod impl_const;

use alloc::vec::Vec;

use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
use syn::{
    Attribute, Expr, GenericParam, Generics, ImplItem, Item, ItemConst, Meta, Signature, TraitItem,
    Type, TypeParamBound, WherePredicate, parse, parse2, punctuated::Punctuated, token::Plus,
};

pub fn unconst(_attr: TokenStream, item: TokenStream) -> TokenStream {
    match parse::<Item>(item).unwrap() {
        Item::Const(mut r#const) => {
            lazylock(&mut r#const);
            quote!(#r#const).into()
        }
        Item::Fn(mut r#fn) => {
            unconst_sig(&mut r#fn.sig);
            quote!(#r#fn).into()
        }
        Item::Enum(mut r#enum) => {
            unconst_attrs(&mut r#enum.attrs);
            unconst_generics(&mut r#enum.generics);
            quote!(#r#enum).into()
        }
        Item::Impl(mut r#impl) => {
            unconst_generics(&mut r#impl.generics);
            for item in r#impl.items.iter_mut() {
                match item {
                    ImplItem::Fn(r#fn) => unconst_sig(&mut r#fn.sig),
                    _ => continue,
                };
            }
            quote!(#r#impl).into()
        }
        Item::Struct(mut r#struct) => {
            unconst_attrs(&mut r#struct.attrs);
            unconst_generics(&mut r#struct.generics);
            quote!(#r#struct).into()
        }
        Item::Trait(mut r#trait) => {
            unconst_attrs(&mut r#trait.attrs);
            unconst_generics(&mut r#trait.generics);
            unconst_bounds(&mut r#trait.supertraits);
            for item in r#trait.items.iter_mut() {
                match item {
                    TraitItem::Fn(r#fn) => unconst_sig(&mut r#fn.sig),
                    _ => continue,
                };
            }
            quote!(#r#trait).into()
        }
        Item::Type(mut r#type) => {
            unconst_generics(&mut r#type.generics);
            quote!(#r#type).into()
        }
        Item::Verbatim(mut ts) => {
            unconst_impl_const(&mut ts);
            ts.into()
        }
        _ => panic!("Input must be one of const/fn/enum/struct/trait/impl"),
    }
}

fn lazylock(r#const: &mut ItemConst) {
    let ty = &r#const.ty;
    let ty = quote!(std::sync::LazyLock<#ty>);
    let ty = parse2::<Type>(ty).unwrap();
    *r#const.ty = ty;
    let expr = r#const.expr.as_mut();
    auto_clone::auto_clone(expr);
    let expr = quote!(std::sync::LazyLock::new(|| #expr));
    let expr = parse2::<Expr>(expr).unwrap();
    *r#const.expr = expr;
}

fn unconst_attrs(attrs: &mut Vec<Attribute>) {
    let mut srtta = Vec::new();
    while let Some(mut attr) = attrs.pop() {
        match &mut attr.meta {
            Meta::Path(path) => {
                if path.get_ident().unwrap() != "const_trait" {
                    srtta.push(attr);
                }
            }
            Meta::List(list) => {
                let segment = list.path.segments.first_mut().unwrap();
                if segment.ident == "derive_const" {
                    segment.ident = Ident::new("derive", segment.ident.span());
                }
                srtta.push(attr);
            }
            _ => {
                srtta.push(attr);
            }
        }
    }
    while let Some(attr) = srtta.pop() {
        attrs.push(attr);
    }
}

fn unconst_sig(signature: &mut Signature) {
    signature.constness = None;
    unconst_generics(&mut signature.generics);
}

fn unconst_generics(generics: &mut Generics) {
    for param in generics.params.iter_mut() {
        match param {
            GenericParam::Type(param) => unconst_bounds(&mut param.bounds),
            _ => continue,
        }
    }
    if let Some(r#where) = generics.where_clause.as_mut() {
        for predicate in r#where.predicates.iter_mut() {
            match predicate {
                WherePredicate::Type(pred) => unconst_bounds(&mut pred.bounds),
                _ => continue,
            }
        }
    }
}

fn unconst_bounds(bounds: &mut Punctuated<TypeParamBound, Plus>) {
    for bound in bounds.iter_mut() {
        match bound {
            TypeParamBound::Verbatim(tt) => {
                *tt = core::mem::take(tt).into_iter().skip(1).collect();
            }
            _ => continue,
        }
    }
}

fn unconst_impl_const(ts: &mut proc_macro2::TokenStream) {
    if let Ok(mut impl_const) = parse2::<impl_const::ItemImplConst>(ts.clone()) {
        impl_const.constness = None;
        for item in r#impl_const.items.iter_mut() {
            match item {
                ImplItem::Fn(r#fn) => unconst_sig(&mut r#fn.sig),
                _ => continue,
            };
        }
        unconst_generics(&mut r#impl_const.generics);
        *ts = quote!(#impl_const);
    }
}