use proc_macro::TokenStream;
use quote::format_ident;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::GenericParam;
use syn::Token;
use syn::TypeParam;
use syn::Visibility;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_attribute]
pub fn one_user(attr: TokenStream, input: TokenStream) -> TokenStream {
let sinput = input.to_string();
let input = parse_macro_input!(input as DeriveInput);
{
let d = if let syn::Data::Struct(d) = input.data {
d
} else {
panic!("Macro only works on structs at the moment!");
};
let d = d.fields;
assert!(
d.iter().any(|field| match field.vis {
Visibility::Public(_) => false,
_ => true,
}),
"Struct must have at least one private field!"
);
}
let name = input.ident;
let where_clause_preds = input.generics.where_clause.map(|x|x.predicates);
let generics_defs = {
let mut generics_defs = input.generics.params;
if !generics_defs.is_empty() {
generics_defs.push_punct(syn::token::Comma::default());
}
generics_defs
};
let generics: Punctuated<GenericParam, Token![,]> = {
let mut generics = generics_defs
.clone()
.into_iter()
.map(|x| {
match x {
GenericParam::Const(val) => GenericParam::Type(TypeParam::from(val.ident)), _ => x,
}
})
.collect::<Punctuated<GenericParam, _>>();
if !generics.is_empty() {
generics.push_punct(syn::token::Comma::default());
}
generics
};
let mod_name = format_ident!("{}_binder", name.to_string().to_lowercase());
let bouncer_name = format_ident!("{}Bouncer", name.to_string());
let unbound_name = format_ident!("Unbound{}", name.to_string());
let bound_name = format_ident!("Bound{}", name.to_string());
let mut_bound_name = format_ident!("MutBound{}", name.to_string());
let (num_slots, pub_defs): (usize, _) = if attr.is_empty() {
(
1,
quote! {
pub type #bouncer_name = #mod_name::BOUNCER<0>;
pub type #unbound_name<#generics_defs> = #mod_name::Unbound<#generics>;
pub type #bound_name<'bound_lifetime, #generics_defs> = #mod_name::Bound<'bound_lifetime, #generics 0>;
pub type #mut_bound_name<'bound_lifetime, #generics_defs> = #mod_name::MutBound<'bound_lifetime, #generics 0>;
},
)
} else {
(
attr.to_string()
.parse()
.expect("Expecting either no args or a single numerical arg, the number of slots!"),
quote! {
pub type #bouncer_name<const SLOT: usize> = #mod_name::BOUNCER<SLOT>;
pub type #unbound_name<#generics_defs> = #mod_name::Unbound<#generics>;
pub type #bound_name<'bound_lifetime, #generics_defs const SLOT: usize> = #mod_name::Bound<'bound_lifetime, #generics SLOT>;
pub type #mut_bound_name<'bound_lifetime, #generics_defs const SLOT: usize> = #mod_name::MutBound<'bound_lifetime, #generics SLOT>;
},
)
};
let out = quote! {
mod #mod_name {
use super::*;
const NSLOTS: usize = #num_slots;
type Usable<#generics_defs> = super::#name<#generics>;
pub trait OnBind {
fn on_bind<const SLOT: usize>(&self);
}
use bitvec::prelude::*;
use std::{
ops::{Deref, DerefMut},
sync::{atomic::AtomicUsize, Mutex},
};
lazy_static! {
static ref BOUNCER_GUARD: Mutex<BitArr!(for NSLOTS, in u8)> = Mutex::new(BitArray::ZERO); pub static ref LAST_SLOT: AtomicUsize = AtomicUsize::new(0);
}
pub struct BOUNCER<const SLOT: usize>(());
impl<const SLOT: usize> BOUNCER<SLOT> {
#[inline]
pub fn new() -> Self {
if SLOT >= NSLOTS {
panic!("Bouncer slot should be available, it was not!");
}
let mut lck = BOUNCER_GUARD.try_lock().expect("Acquring lock to create bouncer!");
if lck.get(SLOT).expect("Bouncer slot should be available, it was not!") == false {
lck.set(SLOT, true);
BOUNCER(())
} else {
panic!("Bouncer already created!");
}
}
}
pub struct MutBound<'bound_lifetime, #generics_defs const SLOT: usize>(&'bound_lifetime mut Usable<#generics>, &'bound_lifetime mut BOUNCER<SLOT>) where #where_clause_preds;
impl<#generics_defs const SLOT: usize> Deref for MutBound<'_, #generics SLOT>
where #where_clause_preds {
type Target = Usable<#generics>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<#generics_defs const SLOT: usize> DerefMut for MutBound<'_, #generics SLOT>
where #where_clause_preds {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub struct Bound<'bound_lifetime, #generics_defs const SLOT: usize>(&'bound_lifetime Usable<#generics>, &'bound_lifetime mut BOUNCER<SLOT>) where #where_clause_preds;
impl<#generics_defs const SLOT: usize> Deref for Bound<'_, #generics SLOT>
where #where_clause_preds {
type Target = Usable<#generics>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub struct Unbound<#generics_defs>(Usable<#generics>)
where
Usable<#generics>: OnBind, #where_clause_preds;
impl<#generics_defs> Unbound<#generics>
where #where_clause_preds {
#[inline]
pub fn from(val: Usable<#generics>) -> Unbound<#generics> {
Unbound(val)
} #[inline]
pub fn bind_mut<'bound_lifetime, const SLOT: usize>(&'bound_lifetime mut self, bn: &'bound_lifetime mut BOUNCER<SLOT>) -> MutBound<'bound_lifetime, #generics SLOT> {
self.0.on_bind::<SLOT>();
LAST_SLOT.store(SLOT, core::sync::atomic::Ordering::SeqCst);
MutBound(&mut self.0, bn)
}
#[inline]
pub fn bind<'bound_lifetime, const SLOT: usize>(&'bound_lifetime self, bn: &'bound_lifetime mut BOUNCER<SLOT>) -> Bound<'bound_lifetime, #generics SLOT> {
self.0.on_bind::<SLOT>();
LAST_SLOT.store(SLOT, core::sync::atomic::Ordering::SeqCst);
Bound(&self.0, bn)
}
}
}
};
let out = format!("{}\n{}\n{}", pub_defs.to_string(), out.to_string(), sinput);
return out.parse().expect("Generated valid tokens!");
}