use proc_macro2::{Ident, Span, TokenStream};
use quote::{ToTokens, quote};
use crate::class::data_models::group_export::FieldGroup;
use crate::class::{FieldExport, FieldVar};
use crate::util::{KvParser, error};
pub struct Field {
pub name: Ident,
pub ty: venial::TypeExpr,
pub default_val: Option<FieldDefault>,
pub var: Option<FieldVar>,
pub export: Option<FieldExport>,
pub group: Option<FieldGroup>,
pub subgroup: Option<FieldGroup>,
pub is_onready: bool,
pub is_oneditor: bool,
pub is_phantomvar: bool,
#[cfg(feature = "register-docs")] #[cfg_attr(published_docs, doc(cfg(feature = "register-docs")))]
pub attributes: Vec<venial::Attribute>,
pub span: Span,
}
impl Field {
pub fn new(field: &venial::NamedField) -> Self {
Self {
name: field.name.clone(),
ty: field.ty.clone(),
default_val: None,
var: None,
export: None,
group: None,
subgroup: None,
is_onready: false,
is_oneditor: false,
is_phantomvar: false,
#[cfg(feature = "register-docs")] #[cfg_attr(published_docs, doc(cfg(feature = "register-docs")))]
attributes: field.attributes.clone(),
span: field.span(),
}
}
pub fn set_default_val_if(
&mut self,
default_expr: impl FnOnce() -> TokenStream,
precondition: FieldCond,
parser: &KvParser,
errors: &mut Vec<venial::Error>,
) {
debug_assert!(
self.default_val.is_none(),
"default already set; check precondition"
);
let span = parser.span();
let is_well_formed = self.ensure_preconditions(precondition, span, errors);
let default_val = if is_well_formed {
default_expr()
} else {
quote! { todo!() }
};
self.default_val = Some(FieldDefault { default_val, span });
}
fn ensure_preconditions(
&self,
cond: FieldCond,
span: Span,
errors: &mut Vec<venial::Error>,
) -> bool {
let prev_size = errors.len();
if self.default_val.is_some() {
errors.push(error!(
span,
"#[init] can have at most one key among `val|node|load`"
));
}
match cond {
FieldCond::IsOnReady if !self.is_onready => {
errors.push(error!(
span,
"used #[init(…)] pattern requires field type `OnReady<T>`"
));
}
FieldCond::IsOnEditor if !self.is_oneditor => {
errors.push(error!(
span,
"used #[init(…)] pattern requires field type `OnEditor<T>`"
));
}
_ => {}
}
errors.len() == prev_size
}
}
pub enum FieldCond {
IsOnReady,
IsOnEditor,
}
#[derive(Clone)]
pub struct FieldDefault {
pub default_val: TokenStream,
pub span: Span,
}
impl ToTokens for FieldDefault {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.default_val.to_tokens(tokens)
}
}