use std::marker::PhantomData;
use proc_macro2::{Span, TokenStream};
use quote::quote;
#[doc(hidden)]
pub fn panicking_uninit_value<T>() -> T {
panic!("stageleft: tried to use uninitialized free variable")
}
pub struct QuoteTokens {
pub prelude: Option<TokenStream>,
pub expr: Option<TokenStream>,
}
pub fn get_final_crate_name(crate_name: &str) -> TokenStream {
let final_crate = proc_macro_crate::crate_name(crate_name).unwrap_or_else(|_| {
panic!("Expected consumer `{crate_name}` package to be present in `Cargo.toml`")
});
match final_crate {
proc_macro_crate::FoundCrate::Itself => {
if std::env::var("CARGO_BIN_NAME").is_ok() {
let underscored = crate_name.replace('-', "_");
let underscored_ident = syn::Ident::new(&underscored, Span::call_site());
quote! { #underscored_ident }
} else {
quote! { crate }
}
}
proc_macro_crate::FoundCrate::Name(name) => {
let ident = syn::Ident::new(&name, Span::call_site());
quote! { #ident }
}
}
}
thread_local! {
pub(crate) static MACRO_TO_CRATE: std::cell::RefCell<Option<(String, String)>> = const { std::cell::RefCell::new(None) };
}
pub fn set_macro_to_crate(macro_name: impl Into<String>, crate_name: impl Into<String>) {
MACRO_TO_CRATE.with(|cell| {
*cell.borrow_mut() = Some((macro_name.into(), crate_name.into()));
});
}
pub trait ParseFromLiteral {
fn parse_from_literal(literal: &syn::Expr) -> Self;
}
macro_rules! impl_parse_from_literal_numeric {
($($ty:ty),*) => {
$(
impl ParseFromLiteral for $ty {
fn parse_from_literal(literal: &syn::Expr) -> Self {
match literal {
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Int(lit_int),
..
}) => lit_int.base10_parse().unwrap(),
_ => panic!("Expected literal"),
}
}
}
)*
};
}
impl ParseFromLiteral for bool {
fn parse_from_literal(literal: &syn::Expr) -> Self {
match literal {
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Bool(lit_bool),
..
}) => lit_bool.value(),
_ => panic!("Expected literal"),
}
}
}
impl_parse_from_literal_numeric!(i8, i16, i32, i64, i128, isize);
impl_parse_from_literal_numeric!(u8, u16, u32, u64, u128, usize);
pub trait FreeVariableWithContextWithProps<Ctx, Props> {
type O;
fn to_tokens(self, ctx: &Ctx) -> (QuoteTokens, Props)
where
Self: Sized;
fn uninitialized(&self, _ctx: &Ctx) -> fn() -> Self::O {
panicking_uninit_value::<Self::O>
}
}
pub trait FreeVariableWithContext<Ctx>: FreeVariableWithContextWithProps<Ctx, ()> {
fn to_tokens(self, ctx: &Ctx) -> QuoteTokens
where
Self: Sized,
{
FreeVariableWithContextWithProps::to_tokens(self, ctx).0
}
fn uninitialized(
&self,
ctx: &Ctx,
) -> fn() -> <Self as FreeVariableWithContextWithProps<Ctx, ()>>::O {
FreeVariableWithContextWithProps::uninitialized(self, ctx)
}
}
impl<Ctx, T: FreeVariableWithContextWithProps<Ctx, ()>> FreeVariableWithContext<Ctx> for T {}
pub trait FreeVariable<O>: FreeVariableWithContext<(), O = O> {
fn to_tokens(self) -> QuoteTokens
where
Self: Sized,
{
FreeVariableWithContextWithProps::to_tokens(self, &()).0
}
fn uninitialized(&self) -> fn() -> O {
panicking_uninit_value::<O>
}
}
impl<O, T: FreeVariableWithContext<(), O = O>> FreeVariable<O> for T {}
macro_rules! impl_free_variable_from_literal_numeric {
($($ty:ty),*) => {
$(
impl<Ctx> FreeVariableWithContextWithProps<Ctx, ()> for $ty {
type O = $ty;
fn to_tokens(self, _ctx: &Ctx) -> (QuoteTokens, ()) {
(QuoteTokens {
prelude: None,
expr: Some(quote!(#self))
}, ())
}
}
impl<'a, Ctx> crate::QuotedWithContextWithProps<'a, $ty, Ctx, ()> for $ty {}
)*
};
}
impl_free_variable_from_literal_numeric!(i8, i16, i32, i64, i128, isize);
impl_free_variable_from_literal_numeric!(u8, u16, u32, u64, u128, usize);
impl<Ctx> FreeVariableWithContextWithProps<Ctx, ()> for &str {
type O = &'static str;
fn to_tokens(self, _ctx: &Ctx) -> (QuoteTokens, ()) {
(
QuoteTokens {
prelude: None,
expr: Some(quote!(#self)),
},
(),
)
}
}
impl<Ctx> FreeVariableWithContextWithProps<Ctx, ()> for String {
type O = &'static str;
fn to_tokens(self, _ctx: &Ctx) -> (QuoteTokens, ()) {
(
QuoteTokens {
prelude: None,
expr: Some(quote!(#self)),
},
(),
)
}
}
pub struct Import<T> {
module_path: &'static str,
crate_name: &'static str,
path: &'static str,
as_name: &'static str,
_phantom: PhantomData<T>,
}
impl<T> Copy for Import<T> {}
impl<T> Clone for Import<T> {
fn clone(&self) -> Self {
*self
}
}
pub fn create_import<T>(
module_path: &'static str,
crate_name: &'static str,
path: &'static str,
as_name: &'static str,
_unused_type_check: T,
) -> Import<T> {
Import {
module_path,
crate_name,
path,
as_name,
_phantom: PhantomData,
}
}
impl<T, Ctx> FreeVariableWithContextWithProps<Ctx, ()> for Import<T> {
type O = T;
fn to_tokens(self, _ctx: &Ctx) -> (QuoteTokens, ()) {
let final_crate_root = get_final_crate_name(self.crate_name);
let module_path = syn::parse_str::<syn::Path>(self.module_path).unwrap();
let parsed = syn::parse_str::<syn::Path>(self.path).unwrap();
let as_ident = syn::Ident::new(self.as_name, Span::call_site());
(
QuoteTokens {
prelude: Some(quote!(use #final_crate_root::#module_path::#parsed as #as_ident;)),
expr: None,
},
(),
)
}
}
pub fn type_hint<O>(v: O) -> O {
v
}
pub fn fn0_type_hint<'a, O>(f: impl Fn() -> O + 'a) -> impl Fn() -> O + 'a {
f
}
pub fn fn1_type_hint<'a, I, O>(f: impl Fn(I) -> O + 'a) -> impl Fn(I) -> O + 'a {
f
}
pub fn fn1_borrow_type_hint<'a, I, O>(f: impl Fn(&I) -> O + 'a) -> impl Fn(&I) -> O + 'a {
f
}
pub fn fn2_type_hint<'a, I1, I2, O>(f: impl Fn(I1, I2) -> O + 'a) -> impl Fn(I1, I2) -> O + 'a {
f
}
pub fn fn2_borrow_type_hint<'a, I1, I2, O>(
f: impl Fn(&I1, &I2) -> O + 'a,
) -> impl Fn(&I1, &I2) -> O + 'a {
f
}
pub fn fn2_borrow_mut_type_hint<'a, I1, I2, O>(
f: impl Fn(&mut I1, I2) -> O + 'a,
) -> impl Fn(&mut I1, I2) -> O + 'a {
f
}
pub fn fnmut0_type_hint<'a, O>(f: impl FnMut() -> O + 'a) -> impl FnMut() -> O + 'a {
f
}
pub fn fnmut1_type_hint<'a, I, O>(f: impl FnMut(I) -> O + 'a) -> impl FnMut(I) -> O + 'a {
f
}
pub fn fnmut1_borrow_type_hint<'a, I, O>(f: impl FnMut(&I) -> O + 'a) -> impl FnMut(&I) -> O + 'a {
f
}
pub fn fnmut2_type_hint<'a, I1, I2, O>(
f: impl FnMut(I1, I2) -> O + 'a,
) -> impl FnMut(I1, I2) -> O + 'a {
f
}
pub fn fnmut2_borrow_type_hint<'a, I1, I2, O>(
f: impl FnMut(&I1, &I2) -> O + 'a,
) -> impl FnMut(&I1, &I2) -> O + 'a {
f
}