pub use super::prelude::*;
pub use super::boolean::*;
pub use super::repeat::*;
pub use super::syntax::*;
pub(super) use super::paste;
pub(super) use super::paste::{IdentFrag, IdentFragInfallible};
#[derive(Debug, Clone)]
pub struct Context<'c> {
pub top: &'c syn::DeriveInput,
pub template_crate: &'c syn::Path,
pub template_name: Option<&'c syn::Path>,
pub tmetas: &'c PreprocessedMetas,
pub variant: Option<&'c WithinVariant<'c>>,
pub field: Option<&'c WithinField<'c>>,
pub pvariants: &'c [PreprocessedVariant<'c>],
pub definitions: Definitions<'c>,
pub nesting_depth: u16,
pub nesting_parent: Option<(&'c Context<'c>, &'c DefinitionName)>,
}
#[derive(Debug, Clone)]
pub struct PreprocessedVariant<'f> {
pub fields: &'f syn::Fields,
pub pmetas: PreprocessedMetas,
pub pfields: Vec<PreprocessedField>,
}
#[derive(Debug, Clone)]
pub struct PreprocessedField {
pub pmetas: PreprocessedMetas,
}
pub type PreprocessedMetas = Vec<PreprocessedMeta>;
#[derive(Debug, Clone)]
pub struct PreprocessedMeta {
pub path: syn::Path,
pub value: PreprocessedMetaValue,
}
#[derive(Debug, Clone)]
pub enum PreprocessedMetaValue {
Unit,
Value { equals: token::Eq, value: syn::Lit },
List(PreprocessedMetaList),
}
#[derive(Debug, Clone)]
pub struct PreprocessedMetaList {
pub content: Punctuated<PreprocessedMeta, token::Comma>,
}
#[derive(Debug, Clone)]
pub struct WithinVariant<'c> {
pub variant: Option<&'c syn::Variant>,
pub fields: &'c syn::Fields,
pub pmetas: &'c PreprocessedMetas,
pub pfields: &'c [PreprocessedField],
}
#[derive(Debug, Clone)]
pub struct WithinField<'c> {
pub field: &'c syn::Field,
pub pfield: &'c PreprocessedField,
pub index: u32,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Definitions<'c> {
pub here: &'c [&'c Definition<DefinitionBody>],
pub conds: &'c [&'c Definition<DefCondBody>],
pub earlier: Option<&'c Definitions<'c>>,
}
pub enum SpecialInstructions {
EndOfTemplate,
}
pub trait SubstParseContext: Sized {
type NotInPaste: Debug + Copy + Sized;
type NotInBool: Debug + Copy + Sized;
type BoolOnly: Debug + Copy + Sized;
fn not_in_paste(span: &impl Spanned) -> syn::Result<Self::NotInPaste>;
fn not_in_bool(span: &impl Spanned) -> syn::Result<Self::NotInBool>;
fn bool_only(span: &impl Spanned) -> syn::Result<Self::BoolOnly> {
Err(span.error(
"derive-adhoc keyword is a condition - not valid as an expansion",
))
}
type SpecialParseContext: Default;
fn special_before_element_hook(
_special: &mut Self::SpecialParseContext,
_input: ParseStream,
) -> syn::Result<Option<SpecialInstructions>> {
Ok(None)
}
}
pub trait ExpansionOutput: SubstParseContext {
fn append_display<I: Display + Spanned + ToTokens>(&mut self, lit: &I);
fn append_identfrag_toks<I: IdentFrag>(
&mut self,
ident: &I,
) -> Result<(), I::BadIdent>;
fn append_idpath<A, B, I>(
&mut self,
template_entry_span: Span,
pre: A,
ident: &I,
post: B,
) -> Result<(), I::BadIdent>
where
A: FnOnce(&mut TokenAccumulator),
B: FnOnce(&mut TokenAccumulator),
I: IdentFrag;
fn append_syn_litstr(&mut self, v: &syn::LitStr);
fn append_syn_type(&mut self, te_span: Span, v: &syn::Type);
fn append_tokens_with(
&mut self,
np: &Self::NotInPaste,
f: impl FnOnce(&mut TokenAccumulator) -> syn::Result<()>,
) -> syn::Result<()>;
fn append_bool_only(&mut self, bool_only: &Self::BoolOnly) -> !;
fn record_error(&mut self, err: syn::Error);
fn write_error<S: Spanned, M: Display>(&mut self, span: &S, message: M) {
self.record_error(span.error(message));
}
fn append_tokens(
&mut self,
np: &Self::NotInPaste,
tokens: impl ToTokens,
) -> syn::Result<()> {
self.append_tokens_with(np, |out| {
out.append(tokens);
Ok(())
})
}
fn default_subst_meta_as() -> SubstMetaAs<Self>;
}
pub trait Expand<O> {
fn expand(&self, ctx: &Context, out: &mut O) -> syn::Result<()>;
}
pub trait ExpandInfallible<O> {
fn expand(&self, ctx: &Context, out: &mut O);
}
#[derive(Debug)]
pub struct TokenAccumulator(Result<TokenStream, syn::Error>);
impl<'c> Context<'c> {
pub fn is_enum(&self) -> bool {
matches!(self.top.data, syn::Data::Enum(_))
}
pub fn call<T>(
driver: &syn::DeriveInput,
template_crate: &syn::Path,
template_name: Option<&syn::Path>,
f: impl FnOnce(Context) -> syn::Result<T>,
) -> Result<T, syn::Error> {
let tmetas = preprocess_attrs(&driver.attrs)?;
let pvariants_one = |fields| {
let pmetas = tmetas.clone(); let pfields = preprocess_fields(fields)?;
let pvariant = PreprocessedVariant {
fields,
pmetas,
pfields,
};
syn::Result::Ok((Some(()), vec![pvariant]))
};
let union_fields;
let (variant, pvariants) = match &driver.data {
syn::Data::Struct(ds) => pvariants_one(&ds.fields)?,
syn::Data::Union(du) => {
union_fields = syn::Fields::Named(du.fields.clone());
pvariants_one(&union_fields)?
}
syn::Data::Enum(de) => (
None,
de.variants
.iter()
.map(|variant| {
let fields = &variant.fields;
let pmetas = preprocess_attrs(&variant.attrs)?;
let pfields = preprocess_fields(&variant.fields)?;
Ok(PreprocessedVariant {
fields,
pmetas,
pfields,
})
})
.collect::<Result<Vec<_>, syn::Error>>()?,
),
};
let variant = variant.map(|()| WithinVariant {
variant: None, fields: pvariants[0].fields,
pmetas: &pvariants[0].pmetas,
pfields: &pvariants[0].pfields,
});
let ctx = Context {
top: &driver,
template_crate,
template_name,
tmetas: &tmetas,
field: None,
variant: variant.as_ref(),
pvariants: &pvariants,
definitions: Default::default(),
nesting_depth: 0,
nesting_parent: None,
};
f(ctx)
}
pub fn expansion_description(&self) -> impl Display {
let ident = &self.top.ident;
if let Some(templ) = &self.template_name {
format!(
"derive-adhoc expansion of {} for {}",
templ.to_token_stream(),
ident,
)
} else {
format!("derive-adhoc expansion, for {}", ident,)
}
}
}
impl Default for TokenAccumulator {
fn default() -> Self {
TokenAccumulator(Ok(TokenStream::new()))
}
}
impl TokenAccumulator {
pub fn new() -> Self {
Self::default()
}
pub fn with_tokens<R>(
&mut self,
f: impl FnOnce(&mut TokenStream) -> R,
) -> Option<R> {
self.0.as_mut().ok().map(f)
}
pub fn append(&mut self, t: impl ToTokens) {
self.with_tokens(|out| t.to_tokens(out));
}
pub fn tokens(self) -> syn::Result<TokenStream> {
self.0
}
pub fn append_maybe_punct_composable(
&mut self,
val: &(impl ToTokens + ToTokensPunctComposable),
composable: bool,
) {
self.with_tokens(|out| {
if composable {
val.to_tokens_punct_composable(out);
} else {
val.to_tokens(out);
}
});
}
}
impl SubstParseContext for TokenAccumulator {
type NotInPaste = ();
type NotInBool = ();
fn not_in_bool(_: &impl Spanned) -> syn::Result<()> {
Ok(())
}
fn not_in_paste(_: &impl Spanned) -> syn::Result<()> {
Ok(())
}
type BoolOnly = Void;
type SpecialParseContext = ();
}
impl ExpansionOutput for TokenAccumulator {
fn append_display<L: Display + Spanned + ToTokens>(&mut self, lit: &L) {
self.append(lit)
}
fn append_identfrag_toks<I: IdentFrag>(
&mut self,
ident: &I,
) -> Result<(), I::BadIdent> {
self.with_tokens(
|out| ident.frag_to_tokens(out), )
.unwrap_or(Ok(()))
}
fn append_idpath<A, B, I>(
&mut self,
_te_span: Span,
pre: A,
ident: &I,
post: B,
) -> Result<(), I::BadIdent>
where
A: FnOnce(&mut TokenAccumulator),
B: FnOnce(&mut TokenAccumulator),
I: IdentFrag,
{
pre(self);
self.append_identfrag_toks(ident)?;
post(self);
Ok(())
}
fn append_syn_litstr(&mut self, lit: &syn::LitStr) {
self.append(lit);
}
fn append_syn_type(&mut self, _te_span: Span, ty: &syn::Type) {
self.append(ty);
}
fn append_tokens_with(
&mut self,
_not_in_paste: &(),
f: impl FnOnce(&mut TokenAccumulator) -> syn::Result<()>,
) -> syn::Result<()> {
f(self)
}
fn append_bool_only(&mut self, bool_only: &Self::BoolOnly) -> ! {
void::unreachable(*bool_only)
}
fn record_error(&mut self, err: syn::Error) {
if let Err(before) = &mut self.0 {
before.combine(err);
} else {
self.0 = Err(err)
}
}
fn default_subst_meta_as() -> SubstMetaAs<Self> {
SubstMetaAs::tokens((), ())
}
}