use std::hash::{DefaultHasher, Hash, Hasher};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{ToTokens, TokenStreamExt, quote};
use syn::{Path, parse::Parse};
pub fn save(name: &Ident, data: &impl quote::ToTokens) -> TokenStream {
save_impl(name, data.to_token_stream())
}
fn save_impl(name: &Ident, data: TokenStream) -> TokenStream {
let unique_name = macro_name(name);
let mut hasher = DefaultHasher::new();
format!("{:?}", name.span()).hash(&mut hasher);
let hash = hasher.finish();
let internal_name = Ident::new(&format!("{}_{:x}", unique_name, hash), name.span());
quote::quote! {
#[macro_export]
#[doc(hidden)]
macro_rules! #internal_name {
(
$target:path, $context:tt,
) => {
$target!(
$context,
(#data),
);
};
}
#[doc(hidden)]
pub use #internal_name as #unique_name;
}
}
pub fn transfer(crate_name: &str, target: &str, data: &impl quote::ToTokens) -> TokenStream {
transfer_impl(crate_name, target, data.to_token_stream())
}
fn transfer_impl(crate_name: &str, target: &str, data: TokenStream) -> TokenStream {
let crate_name = Ident::new(crate_name, Span::mixed_site());
let target = Ident::new(target, Span::mixed_site());
quote::quote! {
#crate_name::macro_data_transfer!(
#crate_name::#target,
(#data),
);
}
}
pub fn macro_name(name: &Ident) -> Ident {
Ident::new(&format!("__macro_data_{}", name), name.span())
}
pub fn request<T>(path: &Path) -> Transfer<T, Request> {
let mut path = path.clone();
let segment = path.segments.last_mut().expect("path has segments");
segment.ident = macro_name(&segment.ident);
Transfer(path)
}
pub struct Request;
pub struct Load;
pub trait Storage {
type Storage<T>;
}
impl Storage for Request {
type Storage<T> = Path;
}
impl Storage for Load {
type Storage<T> = T;
}
pub struct Transfer<T, S: Storage>(pub S::Storage<T>);
impl<T> ToTokens for Transfer<T, Request> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let path = &self.0;
tokens.append_all(quote! {@load(#path)});
}
}
impl<T: ToTokens> ToTokens for Transfer<T, Load> {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.to_tokens(tokens);
}
}
impl<T: Parse> Parse for Transfer<T, Load> {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
T::parse(input).map(Self)
}
}