use super::FXFieldCtx;
use crate::codegen::constructor::FXConstructor;
use crate::codegen::constructor::FXFieldConstructor;
use crate::codegen::constructor::FXFnConstructor;
use crate::codegen::constructor::FXStructConstructor;
use crate::field_receiver::FXField;
use crate::struct_receiver::args::props::FXStructArgProps;
use crate::struct_receiver::args::FXStructArgs;
use crate::struct_receiver::FXStructReceiver;
use crate::types::impl_details::impl_async::FXAsyncImplementor;
use crate::types::impl_details::impl_plain::FXPlainImplementor;
use crate::types::impl_details::impl_sync::FXSyncImplementor;
use crate::types::impl_details::FXImplDetails;
use delegate::delegate;
use fieldx_aux::FXProp;
use getset::CopyGetters;
use getset::Getters;
use once_cell::unsync::OnceCell;
use proc_macro2::TokenStream;
use quote::format_ident;
use quote::quote;
use quote::quote_spanned;
use quote::ToTokens;
use std::cell::Ref;
use std::cell::RefCell;
use std::cell::RefMut;
use std::collections::HashMap;
use std::fmt::Debug;
use std::rc::Rc;
use std::rc::Weak;
pub trait FXImplementationContext: Debug + Sized {
fn set_codegen_ctx(&mut self, ctx: Weak<FXCodeGenCtx<Self>>);
}
impl FXImplementationContext for () {
fn set_codegen_ctx(&mut self, _ctx: Weak<FXCodeGenCtx<Self>>) {}
}
#[derive(Debug, Getters, CopyGetters)]
pub struct FXCodeGenCtx<ImplCtx = ()>
where
ImplCtx: FXImplementationContext,
{
myself: Weak<Self>,
errors: OnceCell<RefCell<darling::error::Accumulator>>,
tokens: OnceCell<RefCell<TokenStream>>,
user_struct: RefCell<FXStructConstructor>,
#[getset(get = "pub")]
args: FXStructArgs,
#[getset(skip)]
arg_props: Rc<FXStructArgProps<ImplCtx>>,
input: Option<Rc<FXStructReceiver>>,
extra_fields: RefCell<Vec<FXField>>,
unique_id: RefCell<u32>,
impl_details: OnceCell<Box<dyn FXImplDetails<ImplCtx>>>,
field_ctx_table: OnceCell<RefCell<HashMap<syn::Ident, Rc<FXFieldCtx<ImplCtx>>>>>,
impl_ctx: RefCell<ImplCtx>,
}
impl<ImplCtx> FXCodeGenCtx<ImplCtx>
where
ImplCtx: FXImplementationContext,
{
delegate! {
to self.arg_props {
pub fn needs_default(&self) -> FXProp<bool>;
pub fn syncish(&self) -> FXProp<bool>;
pub fn needs_new(&self) -> FXProp<bool>;
}
}
pub fn new(input: FXStructReceiver, args: FXStructArgs, mut impl_ctx: ImplCtx) -> Rc<Self> {
let input = Rc::new(input);
let mut user_struct = FXStructConstructor::new(input.ident().clone());
user_struct
.set_span(input.ident().span())
.set_generics(input.generics().clone())
.set_vis(input.vis().clone())
.add_attributes(input.attrs().iter());
Rc::new_cyclic(|myself| {
impl_ctx.set_codegen_ctx(myself.clone());
Self {
myself: myself.clone(),
arg_props: Rc::new(FXStructArgProps::<ImplCtx>::new(args.clone(), myself.clone())),
args,
user_struct: RefCell::new(user_struct),
errors: OnceCell::new(),
extra_fields: RefCell::new(Vec::new()),
field_ctx_table: OnceCell::new(),
tokens: OnceCell::new(),
unique_id: RefCell::new(0),
impl_details: OnceCell::new(),
input: Some(input),
impl_ctx: RefCell::new(impl_ctx),
}
})
}
pub fn user_struct<'a>(&'a self) -> Ref<'a, FXStructConstructor> {
self.user_struct.borrow()
}
pub fn user_struct_mut<'a>(&'a self) -> RefMut<'a, FXStructConstructor> {
self.user_struct.borrow_mut()
}
fn myself(&self) -> Rc<Self> {
self.myself
.upgrade()
.expect("Context object is gone while trying to upgrade a weak reference")
}
pub fn impl_ctx<'a>(&'a self) -> Ref<'a, ImplCtx> {
self.impl_ctx.borrow()
}
pub fn impl_ctx_mut<'a>(&'a self) -> RefMut<'a, ImplCtx> {
self.impl_ctx.borrow_mut()
}
#[inline(always)]
pub fn input(&self) -> &FXStructReceiver {
self.input.as_ref().unwrap()
}
pub fn arg_props(&self) -> Rc<FXStructArgProps<ImplCtx>> {
self.arg_props.clone()
}
#[inline(always)]
pub fn push_error(&self, err: darling::Error) {
self._errors().borrow_mut().push(err);
}
#[inline(always)]
pub fn ok_or_empty<T: From<TokenStream>>(&self, outcome: darling::Result<T>) -> T {
outcome.unwrap_or_else(|err| {
self.push_error(err);
quote![].into()
})
}
#[inline(always)]
pub fn ok_or_record<T>(&self, outcome: darling::Result<T>) {
if let Err(err) = outcome {
self.push_error(err);
}
}
pub fn exec_or_record(&self, code: impl FnOnce() -> darling::Result<()>) {
if let Err(err) = code() {
self.push_error(err)
}
}
#[inline(always)]
pub fn add_field_decl(&self, field: FXFieldConstructor) {
self.user_struct.borrow_mut().add_field(field);
}
#[inline(always)]
pub fn add_method(&self, method: FXFnConstructor) -> &Self {
self.user_struct.borrow_mut().struct_impl_mut().add_method(method);
self
}
#[inline(always)]
pub fn maybe_add_method(&self, method: Option<FXFnConstructor>) -> &Self {
if let Some(method) = method {
self.user_struct.borrow_mut().struct_impl_mut().add_method(method);
}
self
}
#[inline(always)]
pub fn input_ident(&self) -> &syn::Ident {
self.input().ident()
}
#[inline(always)]
pub fn struct_type_toks(&self) -> TokenStream {
let struct_input = self.input();
let ident = struct_input.ident();
let (_, generic_params, _) = struct_input.generics().split_for_impl();
quote_spanned! {ident.span()=>
#ident #generic_params
}
}
pub fn tokens_extend<T: ToTokens>(&self, toks: T) {
self._tokens().borrow_mut().extend(toks.to_token_stream());
}
#[inline]
pub fn finalize(&self) -> TokenStream {
if let Some(errors) = self.errors.get().map(|e| e.take()) {
match errors.finish() {
Ok(_) => (),
Err(err) => {
self.tokens_extend(err.write_errors());
}
};
}
if let Some(tokens) = self.tokens.get() {
tokens.take()
}
else {
quote![]
}
}
#[inline]
pub fn add_extra_field(&self, field: FXField) {
self.extra_fields.borrow_mut().push(field);
}
pub fn field_ctx_table(&self) -> &RefCell<HashMap<syn::Ident, Rc<FXFieldCtx<ImplCtx>>>> {
self.field_ctx_table.get_or_init(|| {
RefCell::new(
self.extra_fields
.borrow()
.iter()
.chain(self.input().fields().iter().cloned())
.map(|f| {
let field_ident = f.ident().unwrap();
(
field_ident.clone(),
Rc::new(FXFieldCtx::<ImplCtx>::new(f.clone(), self.myself())),
)
})
.collect(),
)
})
}
pub fn ident_field_ctx(self: &Rc<Self>, field_ident: &syn::Ident) -> darling::Result<Rc<FXFieldCtx<ImplCtx>>> {
Ok(self
.field_ctx_table()
.borrow()
.get(field_ident)
.ok_or(darling::Error::custom(format!(
"Field '{field_ident}' not found in context table"
)))?
.clone())
}
pub fn field_ctx(&self, field: &FXField) -> Rc<FXFieldCtx<ImplCtx>> {
let mut field_ctx_table = self.field_ctx_table().borrow_mut();
field_ctx_table
.entry(field.ident().unwrap().clone())
.or_insert_with(|| Rc::new(FXFieldCtx::new(field.clone(), self.myself())))
.clone()
}
pub fn all_field_ctx(&self) -> Vec<Rc<FXFieldCtx<ImplCtx>>> {
self.extra_fields
.borrow()
.iter()
.chain(self.input().fields().iter().cloned())
.map(|f| self.field_ctx(f))
.collect()
}
#[inline(always)]
pub fn struct_generic_params(&self) -> TokenStream {
self.input().generics().split_for_impl().1.to_token_stream()
}
#[allow(dead_code)]
#[inline]
pub fn unique_ident_pfx(&self, prefix: &str) -> syn::Ident {
let new_count = *self.unique_id.borrow() + 1;
let _ = self.unique_id.replace(new_count);
format_ident!("{}_{}", prefix, new_count)
}
#[allow(dead_code)]
#[inline]
pub fn unique_ident(&self) -> syn::Ident {
self.unique_ident_pfx(&format!("__{}_fxsym", self.input_ident()))
}
pub fn impl_details(&self) -> &dyn FXImplDetails<ImplCtx> {
self.impl_details
.get_or_init(|| {
let arg_props = self.arg_props();
if arg_props.mode_async().is_some_and(|p| *p) {
Box::new(FXAsyncImplementor)
}
else if arg_props.mode_sync().is_some_and(|p| *p) || *arg_props.syncish() {
Box::new(FXSyncImplementor)
}
else {
Box::new(FXPlainImplementor)
}
})
.as_ref()
}
#[inline(always)]
fn _errors(&self) -> &RefCell<darling::error::Accumulator> {
self.errors.get_or_init(|| RefCell::new(darling::Error::accumulator()))
}
#[inline(always)]
fn _tokens(&self) -> &RefCell<TokenStream> {
self.tokens.get_or_init(|| RefCell::new(TokenStream::new()))
}
}