use std::any::{Any, TypeId};
use std::fmt;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
pub type CrateFetcher = fn(&str) -> TokenStream;
pub trait Literal: fmt::Debug + Any {
fn constant_value(&self, crates: CrateFetcher) -> TokenStream;
}
impl Literal for u8 {
fn constant_value(&self, _crates: CrateFetcher) -> TokenStream {
proc_macro2::Literal::u8_suffixed(*self).into_token_stream()
}
}
impl Literal for u16 {
fn constant_value(&self, _crates: CrateFetcher) -> TokenStream {
proc_macro2::Literal::u16_suffixed(*self).into_token_stream()
}
}
impl Literal for usize {
fn constant_value(&self, _crates: CrateFetcher) -> TokenStream {
proc_macro2::Literal::usize_suffixed(*self).into_token_stream()
}
}
impl Literal for bool {
fn constant_value(&self, _crates: CrateFetcher) -> TokenStream {
if *self {
quote! { true }
} else {
quote! { false }
}
}
}
pub struct DynLiteral {
literal: Box<dyn Literal>,
clone: fn(&dyn Literal) -> Box<dyn Literal>,
}
impl DynLiteral {
pub fn new<L: Literal + Clone>(val: L) -> Self {
Self {
literal: Box::new(val),
clone: Self::dyn_clone::<L>,
}
}
fn dyn_clone<L: Literal + Clone>(val: &dyn Literal) -> Box<dyn Literal> {
assert!(TypeId::of::<L>() == val.type_id(), "Wrong type");
let val = val as *const dyn Literal as *const L;
let val = unsafe { &*val };
Box::new(val.clone())
}
}
impl Clone for DynLiteral {
fn clone(&self) -> Self {
let cloned = (self.clone)(self.literal.as_ref());
Self {
literal: cloned,
clone: self.clone,
}
}
}
impl fmt::Debug for DynLiteral {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.literal.fmt(f)
}
}
impl Literal for DynLiteral {
fn constant_value(&self, crates: CrateFetcher) -> TokenStream {
self.literal.constant_value(crates)
}
}
#[derive(Debug, Clone)]
pub enum Arg<StackT> {
StackValue(StackT),
Literal(DynLiteral),
}