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 pmetas: &'c meta::PreprocessedMetas,
pub variant: Option<&'c WithinVariant<'c>>,
pub field: Option<&'c WithinField<'c>>,
pub within_loop: WithinLoop,
pub pvariants: &'c [PreprocessedVariant<'c>],
pub definitions: Definitions<'c>,
pub nesting_depth: u16,
pub nesting_parent: Option<(&'c Context<'c>, &'c DefinitionName)>,
}
#[derive(Debug)]
pub struct PreprocessedVariant<'c> {
pub fields: &'c syn::Fields,
pub pmetas: &'c meta::PreprocessedMetas,
pub pfields: Vec<PreprocessedField>,
}
#[derive(Debug)]
pub struct PreprocessedField {
pub pmetas: meta::PreprocessedMetas,
}
#[derive(Debug, Clone)]
pub struct WithinVariant<'c> {
pub variant: Option<&'c syn::Variant>,
pub fields: &'c syn::Fields,
pub pmetas: &'c meta::PreprocessedMetas,
pub pfields: &'c [PreprocessedField],
pub index: u32,
}
#[derive(Debug, Clone)]
pub struct WithinField<'c> {
pub field: &'c syn::Field,
pub pfield: &'c PreprocessedField,
pub index: u32,
}
#[derive(Debug, Clone, Copy)]
pub enum WithinLoop {
None,
When,
Body,
}
#[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 NotInConcat: Debug + Copy + Sized;
type NotInBool: Debug + Copy + Sized;
type ConcatOnly: Debug + Copy + Sized;
type BoolOnly: Debug + Copy + Sized;
const IS_BOOL: bool = false;
type DbgContent: Parse + Debug + AnalyseRepeat + meta::FindRecogMetas;
fn not_in_paste(span: &impl Spanned) -> syn::Result<Self::NotInPaste>;
fn not_in_concat(span: &impl Spanned) -> syn::Result<Self::NotInConcat>;
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-deftly keyword is a condition - not valid as an expansion",
))
}
fn concat_only(span: &impl Spanned) -> syn::Result<Self::ConcatOnly> {
Err(span
.error("derive-deftly keyword is only allowed within ${concat }"))
}
fn meta_recog_usage() -> meta::Usage;
type SpecialParseContext: Default;
fn special_before_element_hook(
_special: &mut Self::SpecialParseContext,
_input: ParseStream,
) -> syn::Result<Option<SpecialInstructions>> {
Ok(None)
}
fn parse_maybe_within_parens<T>(
input: ParseStream,
f: impl FnOnce(ParseStream) -> syn::Result<T>,
) -> syn::Result<T> {
if Self::IS_BOOL {
let inner;
let _ = parenthesized!(inner in input);
f(&inner)
} else {
f(input)
}
}
fn parse_maybe_comma(input: ParseStream) -> syn::Result<()> {
if Self::IS_BOOL {
let _: Token![,] = input.parse()?;
}
Ok(())
}
fn missing_keyword_arguments(kw_span: Span) -> syn::Result<Void> {
Err(kw_span.error(format_args!(
"missing parameters to expansion keyword (NB: argument must be within {{ }})",
)))
}
}
#[allow(type_alias_bounds)]
pub type AllowTokens<O: SubstParseContext> = (
<O as SubstParseContext>::NotInPaste,
<O as SubstParseContext>::NotInConcat,
);
pub trait SubstParseContextExt: SubstParseContext {
fn allow_tokens(span: &impl Spanned) -> syn::Result<AllowTokens<Self>>;
}
impl<O: SubstParseContext> SubstParseContextExt for O {
fn allow_tokens(span: &impl Spanned) -> syn::Result<AllowTokens<Self>> {
let span = span.span();
Ok((O::not_in_paste(&span)?, (O::not_in_concat(&span)?)))
}
}
pub trait ExpansionOutput: SubstParseContext {
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,
grouping: Grouping,
) -> 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,
mut v: syn::Type,
mut grouping: Grouping,
) {
loop {
let (inner, add_grouping) = match v {
syn::Type::Paren(inner) => (inner.elem, Grouping::Parens),
syn::Type::Group(inner) => (inner.elem, Grouping::Invisible),
_ => break,
};
v = *inner;
grouping = cmp::max(grouping, add_grouping);
}
if let syn::Type::Path(tp) = &mut v {
typepath_add_missing_argument_colons(tp, te_span);
}
self.append_syn_type_inner(te_span, v, grouping)
}
fn append_syn_type_inner(
&mut self,
te_span: Span,
v: syn::Type,
grouping: Grouping,
);
fn append_tokens_with(
&mut self,
allow_tokens: &AllowTokens<Self>,
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,
allow_tokens: &AllowTokens<Self>,
tokens: impl ToTokens,
) -> syn::Result<()> {
self.append_tokens_with(allow_tokens, |out| {
out.append(tokens);
Ok(())
})
}
fn new_with_span(kw_span: Span) -> Self;
fn default_subst_meta_as(kw: Span) -> syn::Result<meta::SubstAs<Self>> {
Err(kw.error("missing `as ...` in meta expansion"))
}
fn ignore_impl(self) -> syn::Result<()>;
fn dbg_expand(
&mut self,
kw_span: Span,
ctx: &Context,
msg: &mut String,
content: &Self::DbgContent,
) -> fmt::Result;
}
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 expansion_description(&self) -> impl Display {
let ident = &self.top.ident;
if let Some(templ) = &self.template_name {
format!(
"derive-deftly expansion of {} for {}",
templ.to_token_stream(),
ident,
)
} else {
format!("derive-deftly 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 NotInConcat = ();
type NotInBool = ();
type DbgContent = Template<Self>;
fn not_in_paste(_: &impl Spanned) -> syn::Result<()> {
Ok(())
}
fn not_in_concat(_: &impl Spanned) -> syn::Result<()> {
Ok(())
}
fn not_in_bool(_: &impl Spanned) -> syn::Result<()> {
Ok(())
}
fn meta_recog_usage() -> meta::Usage {
meta::Usage::Value
}
type BoolOnly = Void;
type ConcatOnly = Void;
type SpecialParseContext = ();
}
impl ExpansionOutput for TokenAccumulator {
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,
grouping: Grouping,
) -> Result<(), I::BadIdent>
where
A: FnOnce(&mut TokenAccumulator),
B: FnOnce(&mut TokenAccumulator),
I: IdentFrag,
{
let inner = match self.with_tokens(|_outer| {
let mut inner = TokenAccumulator::new();
pre(&mut inner);
inner.append_identfrag_toks(ident)?;
post(&mut inner);
Ok(inner)
}) {
None => return Ok(()), Some(Err(e)) => return Err(e),
Some(Ok(ta)) => ta,
};
match inner.tokens() {
Ok(ts) => self.append(grouping.surround(ts)),
Err(e) => self.record_error(e),
}
Ok(())
}
fn append_syn_litstr(&mut self, lit: &syn::LitStr) {
self.append(lit);
}
fn append_syn_type_inner(
&mut self,
_te_span: Span,
ty: syn::Type,
grouping: Grouping,
) {
self.append(grouping.surround(ty));
}
fn append_tokens_with(
&mut self,
(_not_in_paste, _not_in_concat): &((), ()),
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 new_with_span(_kw_span: Span) -> Self {
Self::new()
}
fn ignore_impl(self) -> syn::Result<()> {
self.0.map(|_: TokenStream| ())
}
fn dbg_expand(
&mut self,
_kw_span: Span,
ctx: &Context,
msg: &mut String,
content: &Template<TokenAccumulator>,
) -> fmt::Result {
let mut child = TokenAccumulator::new();
content.expand(ctx, &mut child);
let child = child.tokens();
match &child {
Err(e) => write!(msg, "/* ERROR: {} */", e)?,
Ok(y) => write!(msg, "{}", y)?,
}
match child {
Ok(y) => self.append(y),
Err(e) => self.record_error(e),
}
Ok(())
}
}