use proc_macro2::TokenStream;
use quote::format_ident;
use quote::quote;
use syn::Ident;
use crate::common;
use crate::factory::Factory;
use crate::paths;
use super::lock::SingletonLock;
pub(super) struct SingletonTraitFactory<'f> {
static_ident: &'f Ident,
type_name: &'f Ident,
lock: &'f SingletonLock,
}
impl<'f> SingletonTraitFactory<'f> {
pub fn new(static_ident: &'f Ident, type_name: &'f Ident, lock: &'f SingletonLock) -> Self {
Self {
static_ident,
type_name,
lock,
}
}
fn impl_init_singleton(&self) -> TokenStream {
let static_ident = self.static_ident;
let type_name = self.type_name;
let anyhow = paths::anyhow_path();
let err_msg =
common::create_lit_str(format!("{}: singleton: already initialized", type_name));
let doc = common::create_doc(format!("Initialize the singleton for {}.", type_name));
let inner_ident = format_ident!("inner");
let new_lock_expr = self.lock.to_new_lock_expr(&inner_ident);
quote! {
#doc
#[automatically_derived]
fn init_singleton(#inner_ident: Self::Inner) -> #anyhow::Result<()> {
if #static_ident.set(#new_lock_expr).is_err() {
Err(anyhow::anyhow!(#err_msg))
} else {
Ok(())
}
}
}
}
fn impl_use_singleton(&self) -> TokenStream {
let static_ident = self.static_ident;
let type_name = self.type_name;
let blockz = paths::blockz_path();
let doc = common::create_doc(format!(
"Run an async function using an immutable {}.",
type_name
));
let lock_ident = format_ident!("inner_lock");
let lock_guard_expr = self.lock.to_guard(&lock_ident);
quote! {
#doc
#[automatically_derived]
async fn use_singleton<F, R>(clojure: F) -> R
where
F: for<'c> #blockz::singleton::SingletonFn<'c, #type_name, R> + Send,
R: Send,
{
let #lock_ident = #static_ident.get().unwrap();
let inner_guard = #lock_guard_expr;
let inner_deref: &#type_name = &*inner_guard;
clojure.call_once(inner_deref).await
}
}
}
fn impl_use_mut_singleton(&self) -> TokenStream {
let static_ident = self.static_ident;
let type_name = self.type_name;
let blockz = paths::blockz_path();
let doc = common::create_doc(format!(
"Run an async function using a mutable {}.",
type_name
));
let lock_ident = format_ident!("inner_lock");
let mut_lock_guard_expr = self.lock.to_mut_guard(&lock_ident);
quote! {
#doc
#[automatically_derived]
async fn use_mut_singleton<F, R>(clojure: F) -> R
where
F: for<'c> #blockz::singleton::SingletonFnMut<'c, Self::Inner, R> + Send,
R: Send,
{
let #lock_ident = #static_ident.get().unwrap();
let mut inner_guard = #mut_lock_guard_expr;
let inner_deref: &mut #type_name = &mut *inner_guard;
clojure.call_once(inner_deref).await
}
}
}
fn impl_use_singleton_with_arg(&self) -> TokenStream {
let static_ident = self.static_ident;
let type_name = self.type_name;
let blockz = paths::blockz_path();
let doc = common::create_doc(format!(
"Run an async function using an immutable {} and an argument.",
type_name
));
let lock_ident = format_ident!("inner_lock");
let lock_guard_expr = self.lock.to_guard(&lock_ident);
quote! {
#doc
#[automatically_derived]
async fn use_singleton_with_arg<F, A, R>(clojure: F, arg: A) -> R
where
F: for<'c> #blockz::singleton::SingletonFnWithArg<'c, Self::Inner, A, R> + Send,
A: Send,
R: Send
{
let #lock_ident = #static_ident.get().unwrap();
let inner_guard = #lock_guard_expr;
let inner_deref: &#type_name = &*inner_guard;
clojure.call_once(inner_deref, arg).await
}
}
}
fn impl_use_mut_singleton_with_arg(&self) -> TokenStream {
let static_ident = self.static_ident;
let type_name = self.type_name;
let blockz = paths::blockz_path();
let doc = common::create_doc(format!(
"Run an async function using a mutable {} and an argument.",
type_name
));
let lock_ident = format_ident!("inner_lock");
let mut_lock_guard_expr = self.lock.to_mut_guard(&lock_ident);
quote! {
#doc
#[automatically_derived]
async fn use_mut_singleton_with_arg<F, A, R>(clojure: F, arg: A) -> R
where
F: for<'c> #blockz::singleton::SingletonFnMutWithArg<'c, Self::Inner, A, R> + Send,
A: Send,
R: Send
{
let #lock_ident = #static_ident.get().unwrap();
let mut inner_guard = #mut_lock_guard_expr;
let inner_deref: &mut #type_name = &mut *inner_guard;
clojure.call_once(inner_deref, arg).await
}
}
}
}
impl<'f> Factory for SingletonTraitFactory<'f> {
type Product = TokenStream;
fn build(self) -> Self::Product {
let blockz = paths::blockz_path();
let init_singleton = self.impl_init_singleton();
let use_singleton = self.impl_use_singleton();
let use_singleton_mut = self.impl_use_mut_singleton();
let use_singleton_with_arg = self.impl_use_singleton_with_arg();
let use_singleton_mut_with_arg = self.impl_use_mut_singleton_with_arg();
let type_name = self.type_name;
quote! {
#[async_trait::async_trait]
impl #blockz::singleton::Singleton for #type_name {
type Inner = #type_name;
#init_singleton
#use_singleton
#use_singleton_mut
#use_singleton_with_arg
#use_singleton_mut_with_arg
}
}
}
}