use super::framework::*;
pub use SubstDetails as SD;
pub use TemplateElement as TE;
#[derive(Debug)]
pub struct Template<O: SubstParseContext> {
pub elements: Vec<TemplateElement<O>>,
}
#[derive(Debug)]
pub struct TopTemplate {
pub content: Template<TokenAccumulator>,
}
#[derive(Debug)]
pub struct Argument<O: SubstParseContext = TokenAccumulator>(pub Template<O>);
#[derive(Debug)]
pub struct TemplateWithWhens<O: SubstParseContext> {
pub elements: Vec<TemplateElement<O>>,
}
#[derive(Debug)]
pub enum TemplateElement<O: SubstParseContext> {
Ident(Ident),
LitStr(syn::LitStr),
Literal(syn::Lit, O::NotInPaste),
Punct(Punct, O::NotInPaste),
Group {
delim_span: Span,
delimiter: Delimiter,
template: Template<O>,
not_in_paste: O::NotInPaste,
},
Subst(Subst<O>),
Repeat(RepeatedTemplate<O>),
}
#[derive(Debug)]
pub struct RepeatedTemplate<O: SubstParseContext> {
pub template: Template<O>,
#[allow(clippy::vec_box)]
pub whens: Vec<Box<Subst<BooleanContext>>>,
pub over: RepeatOver,
}
#[derive(Debug)]
pub struct Subst<O: SubstParseContext> {
pub kw_span: Span,
pub sd: SubstDetails<O>,
}
pub type Condition = Subst<BooleanContext>;
#[allow(non_camel_case_types)] #[derive(Debug)]
pub enum SubstDetails<O: SubstParseContext> {
tname(O::NotInBool),
ttype(O::NotInBool),
tdeftype(O::NotInBool),
vname(O::NotInBool),
fname(O::NotInBool),
ftype(O::NotInBool),
fpatname(O::NotInBool),
Vis(SubstVis, O::NotInPaste), tdefkwd(O::NotInBool),
Xmeta(meta::SubstMeta<O>),
tattrs(RawAttr, O::NotInPaste, O::NotInBool),
vattrs(RawAttr, O::NotInPaste, O::NotInBool),
fattrs(RawAttr, O::NotInPaste, O::NotInBool),
tgens(O::NotInPaste),
tdefgens(O::NotInPaste, O::NotInBool),
tgnames(O::NotInPaste, O::NotInBool),
twheres(O::NotInPaste, O::NotInBool),
vpat(SubstVPat, O::NotInPaste, O::NotInBool),
vtype(SubstVType, O::NotInPaste, O::NotInBool),
tdefvariants(Template<TokenAccumulator>, O::NotInPaste, O::NotInBool),
fdefine(Option<Argument>, O::NotInPaste, O::NotInBool),
vdefbody(
Argument<O>,
Template<TokenAccumulator>,
O::NotInPaste,
O::NotInBool,
),
paste(Template<paste::Items>, O::NotInBool),
ChangeCase(Template<paste::Items>, paste::ChangeCase, O::NotInBool),
when(Box<Condition>, O::NotInBool),
define(Definition<DefinitionBody>, O::NotInBool),
defcond(Definition<DefCondBody>, O::NotInBool),
UserDefined(DefinitionName),
False(O::BoolOnly),
True(O::BoolOnly),
not(Box<Condition>, O::BoolOnly),
any(Punctuated<Condition, token::Comma>, O::BoolOnly),
all(Punctuated<Condition, token::Comma>, O::BoolOnly),
is_struct(O::BoolOnly),
is_enum(O::BoolOnly),
is_union(O::BoolOnly),
v_is_unit(O::BoolOnly),
v_is_tuple(O::BoolOnly),
v_is_named(O::BoolOnly),
is_empty(O::BoolOnly, Template<TokenAccumulator>),
approx_equal(O::BoolOnly, [Argument; 2]),
For(RepeatedTemplate<O>, O::NotInBool),
If(SubstIf<O>, O::NotInBool),
select1(SubstIf<O>, O::NotInBool),
ignore(Template<O>, O::NotInBool),
error(ExplicitError, O::NotInBool),
dbg(DbgDumpRequest<O>),
dbg_all_keywords(O::NotInBool),
Crate(O::NotInPaste, O::NotInBool),
}
#[derive(Debug)]
pub struct Definition<B> {
pub name: DefinitionName,
pub body_span: Span,
pub body: B,
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct DefinitionName(syn::Ident);
#[derive(Debug)]
pub enum DefinitionBody {
Normal(Argument),
Paste(Argument<paste::Items>),
}
pub type DefCondBody = Box<Condition>;
#[derive(Debug)]
pub struct ExplicitError {
pub message: syn::LitStr,
}
#[derive(Debug)]
pub struct SubstIf<O: SubstParseContext> {
pub tests: Vec<(Condition, Template<O>)>,
pub otherwise: Option<Box<Template<O>>>,
pub kw_span: Span,
}
#[derive(Debug)]
pub enum SubstVis {
T,
F,
FD,
}
#[derive(Debug)]
pub struct SubstVType {
pub self_: Option<Argument>,
pub vname: Option<Argument>,
}
#[derive(Debug)]
pub struct SubstVPat {
pub vtype: SubstVType,
pub fprefix: Option<Argument<paste::Items>>,
}
#[derive(Debug, Clone)]
pub enum RawAttr {
Default,
Include {
entries: Punctuated<RawAttrEntry, token::Comma>,
},
Exclude {
exclusions: Punctuated<syn::Path, token::Comma>,
},
}
#[derive(Debug, Clone)]
pub struct RawAttrEntry {
pub path: syn::Path,
}
#[derive(Debug)]
pub struct DbgDumpRequest<O: SubstParseContext> {
pub note: Option<String>,
pub content_parsed: Box<O::DbgContent>,
pub content_string: (),
}
pub struct InvalidDefinitionName;
impl ToTokens for DefinitionName {
fn to_tokens(&self, out: &mut TokenStream) {
self.0.to_tokens(out)
}
}
impl TryFrom<syn::Ident> for DefinitionName {
type Error = InvalidDefinitionName;
fn try_from(ident: syn::Ident) -> Result<Self, InvalidDefinitionName> {
let s = ident.to_string();
let c = s.chars().next().expect("identifer was empty string!");
if c.is_lowercase() {
Err(InvalidDefinitionName)
} else {
Ok(DefinitionName(ident))
}
}
}
impl Display for DefinitionName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl Parse for DefinitionName {
fn parse(input: ParseStream) -> syn::Result<Self> {
let ident = input.call(Ident::parse_any)?;
let span = ident.span();
Ok(ident.try_into().map_err(|InvalidDefinitionName| {
span.error(
"invalid name for definition - may not start with lowercase",
)
})?)
}
}
impl Parse for Definition<DefinitionBody> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let name = input.parse()?;
let body_span = input.span();
let mut body: Argument<_> = input.parse()?;
let body = match (|| {
if body.elements.len() != 1 {
return None;
}
let Template { elements } = &mut body.0;
match elements.pop().expect("just checked length") {
TE::Subst(Subst {
kw_span,
sd: SD::paste(items, not_in_bool),
}) => Some(Argument(Template {
elements: vec![TE::Subst(Subst {
kw_span,
sd: SD::paste(items, not_in_bool),
})],
})),
other => {
elements.push(other);
None
}
}
})() {
Some(paste) => DefinitionBody::Paste(paste),
None => DefinitionBody::Normal(body),
};
Ok(Definition {
name,
body_span,
body,
})
}
}
impl Parse for Definition<DefCondBody> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let name = input.parse()?;
let body_span = input.span();
let body = Box::new(input.parse()?);
Ok(Definition {
name,
body_span,
body,
})
}
}
impl<O: SubstParseContext> Spanned for Subst<O> {
fn span(&self) -> Span {
self.kw_span
}
}
impl TopTemplate {
pub fn parse(
input: ParseStream,
beta_enabled: Option<beta::Enabled>,
) -> syn::Result<Self> {
let content = beta::with_maybe_enabled(
beta_enabled,
|| input.parse(),
)?;
Ok(TopTemplate { content })
}
}
impl Deref for TopTemplate {
type Target = Template<TokenAccumulator>;
fn deref(&self) -> &Template<TokenAccumulator> {
&self.content
}
}
impl<O: SubstParseContext> Parse for Template<O> {
fn parse(input: ParseStream) -> syn::Result<Self> {
Template::parse_special(input, &mut Default::default())
}
}
impl<O: SubstParseContext> Template<O> {
fn parse_special(
input: ParseStream,
special: &mut O::SpecialParseContext,
) -> syn::Result<Self> {
TemplateWithWhens::parse_special(input, special)?.try_into()
}
}
impl<O: SubstParseContext> TemplateWithWhens<O> {
fn parse_special(
input: ParseStream,
mut special: &mut O::SpecialParseContext,
) -> syn::Result<Self> {
let mut good = vec![];
let mut errors = ErrorAccumulator::default();
while !input.is_empty() {
let special = &mut special;
match errors.handle_in(|| {
O::special_before_element_hook(special, input)
}) {
Some(None) => {},
None | Some(Some(SpecialInstructions::EndOfTemplate)) => break,
}
errors.handle_in(|| {
let elem = input.parse()?;
good.push(elem);
Ok(special)
});
}
errors.finish_with(TemplateWithWhens { elements: good })
}
}
impl<O: SubstParseContext> Parse for Argument<O> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let la = input.lookahead1();
if la.peek(token::Brace) {
let inner;
let _brace = braced!(inner in input);
Template::parse(&inner)
} else if la.peek(Ident::peek_any)
|| la.peek(Token![$])
|| la.peek(syn::Lit)
{
let element = input.parse()?;
TemplateWithWhens {
elements: vec![element],
}
.try_into()
} else {
Err(la.error())
}
.map(Argument)
}
}
impl<O: SubstParseContext> Deref for Argument<O> {
type Target = Template<O>;
fn deref(&self) -> &Template<O> {
&self.0
}
}
pub enum OrigDollarDeescaped {
NotFound,
Found,
}
pub fn deescape_orig_dollar(
input: ParseStream,
) -> syn::Result<OrigDollarDeescaped> {
input.step(|cursor| {
let (found, rest) = (|| {
let (ident, rest) = cursor.ident()?;
(ident == "orig_dollar").then(|| ())?;
Some((OrigDollarDeescaped::Found, rest))
})()
.unwrap_or((OrigDollarDeescaped::NotFound, *cursor));
Ok((found, rest))
})
}
impl<O: SubstParseContext> Parse for TemplateElement<O> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let input_span = input.span();
let not_in_paste = || O::not_in_paste(&input_span);
let backtracked = input.fork();
Ok(match input.parse()? {
TT::Group(group) => {
let delim_span = group.span_open();
let delimiter = group.delimiter();
let t_parser = |input: ParseStream| Template::parse(input);
let template = t_parser.parse2(group.stream())?;
TE::Group {
not_in_paste: not_in_paste()?,
delim_span,
delimiter,
template,
}
}
TT::Ident(tt) => TE::Ident(tt),
tt @ TT::Literal(..) => match syn::parse2(tt.into())? {
syn::Lit::Str(s) => TE::LitStr(s),
other => TE::Literal(other, not_in_paste()?),
},
TT::Punct(tok) if tok.as_char() == '#' => {
if let Ok(attr) = syn::Attribute::parse_inner(&backtracked) {
if let Some(attr) = attr.first() {
return Err(attr.error(
"inner attributes are reserved syntax, anywhere in derive-deftly templates"
));
}
}
TE::Punct(tok, not_in_paste()?)
}
TT::Punct(tok) if tok.as_char() != '$' => {
TE::Punct(tok, not_in_paste()?)
}
TT::Punct(_dollar) => {
let deescaped = deescape_orig_dollar(input)?;
let la = input.lookahead1();
if la.peek(Token![$]) {
let dollar: Punct = input.parse()?;
match deescaped {
OrigDollarDeescaped::NotFound => {},
OrigDollarDeescaped::Found => {
match deescape_orig_dollar(input)? {
OrigDollarDeescaped::Found => {},
OrigDollarDeescaped::NotFound => {
return Err(dollar.error(
"found `$orig_dollar $` not followed by another orig_dollar!"
))
},
}
},
}
TE::Punct(dollar, not_in_paste()?)
} else if la.peek(token::Paren) {
RepeatedTemplate::parse_in_parens(input)?
} else {
TE::Subst(Subst::parse_after_dollar(la, input, deescaped)?)
}
}
})
}
}
pub trait ParseUsingSubkeywords: Sized + ParseOneSubkeyword {
fn parse(input: ParseStream, kw_span: Span) -> syn::Result<Self> {
let mut out = Self::new_default(kw_span)?;
while !input.is_empty() {
let subkw: IdentAny = input.parse()?;
let _: Token![=] = input.parse()?;
out.process_one_keyword(&subkw, input).unwrap_or_else(|| {
Err(subkw.error("unknown $vpat/$vconstr argument sub-keyword"))
})?;
}
Ok(out)
}
fn new_default(kw_span: Span) -> syn::Result<Self>;
}
pub trait ParseOneSubkeyword: Sized {
fn process_one_keyword(
&mut self,
kw: &syn::Ident,
input: ParseStream,
) -> Option<syn::Result<()>>;
}
fn subkw_parse_store<KO>(
subkw: &syn::Ident,
input: ParseStream,
dest: &mut Option<Argument<KO>>,
) -> syn::Result<()>
where
KO: SubstParseContext,
{
if let Some(_) = &dest {
return Err(
subkw.error("same argument sub-keyword specified more than once")
);
}
*dest = Some(input.parse()?);
Ok(())
}
macro_rules! impl_parse_one_subkeyword { {
$ty:ident $( < $O:ident > )?:
$( ( $($spec:tt)+ ) ),* $(,)?
} => {
impl $(<$O: SubstParseContext>)?
ParseOneSubkeyword for $ty $(<$O>)? {
fn process_one_keyword(&mut self, got: &syn::Ident, ps: ParseStream)
-> Option<syn::Result<()>> {
$( impl_parse_one_subkeyword!{ @ (self, got, ps) @ $($spec)+ } )*
None
}
}
}; { @ $bind:tt @ $exp:ident } => {
impl_parse_one_subkeyword! { @@ $bind @ stringify!($exp), . $exp }
}; { @ $bind:tt @ $exp:literal: . $($field:tt)+ } => {
impl_parse_one_subkeyword! { @@ $bind @ $exp, . $($field)+ }
}; { @ ($self:expr, $got:expr, $ps:expr) @ .. $($substruct:tt)+ } => {
if let Some(r) = $self.$($substruct)+.process_one_keyword($got, $ps) {
return Some(r);
}
}; { @@ ($self:expr, $got:expr, $ps:expr) @ $exp:expr, .$($field:tt)+ } => {
if $got == $exp {
return Some(subkw_parse_store($got, $ps, &mut $self.$($field)+));
}
} }
impl_parse_one_subkeyword! {
SubstVType:
("self": .self_),
(vname),
}
impl_parse_one_subkeyword! {
SubstVPat:
(..vtype),
(fprefix),
}
impl ParseUsingSubkeywords for SubstVType {
fn new_default(_tspan: Span) -> syn::Result<Self> {
Ok(SubstVType {
self_: None,
vname: None,
})
}
}
impl ParseUsingSubkeywords for SubstVPat {
fn new_default(tspan: Span) -> syn::Result<Self> {
Ok(SubstVPat {
vtype: SubstVType::new_default(tspan)?,
fprefix: None,
})
}
}
impl<O: SubstParseContext> Subst<O> {
#[allow(dead_code)] fn parse_entire(input: ParseStream) -> syn::Result<Self> {
let _dollar: Token![$] = input.parse()?;
let deescaped = deescape_orig_dollar(input)?;
let la = input.lookahead1();
Self::parse_after_dollar(la, input, deescaped)
}
fn parse_after_dollar(
la: Lookahead1,
input: ParseStream,
_deescaped: OrigDollarDeescaped,
) -> syn::Result<Self> {
if la.peek(token::Brace) {
let exp;
struct Only<O: SubstParseContext>(Subst<O>);
impl<O: SubstParseContext> Parse for Only<O> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let subst = input.parse()?;
let unwanted: Option<TT> = input.parse()?;
if let Some(unwanted) = unwanted {
return Err(unwanted.error(
"unexpected arguments to expansion keyword",
));
}
Ok(Only(subst))
}
}
let _brace = braced!(exp in input);
let exp = exp.parse()?;
let Only(exp) = exp;
Ok(exp)
} else if la.peek(syn::Ident::peek_any) {
let exp: TokenTree = input.parse()?; let exp = syn::parse2(exp.to_token_stream())?;
Ok(exp)
} else if la.peek(Token![<]) {
let angle: Token![<] = input.parse()?;
let state = paste::AngleBrackets::default();
let mut special = Some(state);
let template = Template::parse_special(input, &mut special)?;
let state = special.unwrap();
state.finish(angle.span())?;
Ok(Subst {
kw_span: angle.span(),
sd: SD::paste(template, O::not_in_bool(&angle)?),
})
} else {
return Err(la.error());
}
}
}
impl<O: SubstParseContext> Parse for Subst<O> {
fn parse<'i>(input: ParseStream<'i>) -> syn::Result<Self> {
let kw: IdentAny = input.parse()?;
let from_sd = |sd| {
Ok(Subst {
sd,
kw_span: kw.span(),
})
};
#[cfg(feature = "bizarre")]
let kw = {
let s = kw.to_string();
let s = s
.strip_suffix("_bizarre")
.ok_or_else(|| kw.error("bizarre mode but not _bizarre"))?;
IdentAny(syn::Ident::new(s, kw.span()))
};
macro_rules! keyword { { $($args:tt)* } => {
keyword_general! { kw from_sd SD; $($args)* }
} }
let not_in_paste = || O::not_in_paste(&kw);
let not_in_bool = || O::not_in_bool(&kw);
let bool_only = || O::bool_only(&kw);
let parse_if = |input| SubstIf::parse(input, kw.span());
let in_parens = |input: ParseStream<'i>| {
let inner;
let _paren = parenthesized!(inner in input);
Ok(inner)
};
let parse_def_body = |input: ParseStream<'i>, m| {
if input.is_empty() {
return Err(kw.error(m));
}
Template::parse(input)
};
let parse_meta =
|input, scope| meta::SubstMeta::parse(input, kw.span(), scope);
keyword! { tname(not_in_bool()?) }
keyword! { ttype(not_in_bool()?) }
keyword! { tdeftype(not_in_bool()?) }
keyword! { vname(not_in_bool()?) }
keyword! { fname(not_in_bool()?) }
keyword! { ftype(not_in_bool()?) }
keyword! { fpatname(not_in_bool()?) }
keyword! { tdefkwd(not_in_bool()?) }
keyword! { "tvis": Vis(SubstVis::T, not_in_paste()?) }
keyword! { "fvis": Vis(SubstVis::F, not_in_paste()?) }
keyword! { "fdefvis": Vis(SubstVis::FD, not_in_paste()?) }
keyword! { is_struct(bool_only()?) }
keyword! { is_enum(bool_only()?) }
keyword! { is_union(bool_only()?) }
keyword! { v_is_unit(bool_only()?) }
keyword! { v_is_tuple(bool_only()?) }
keyword! { v_is_named(bool_only()?) }
keyword! { tgens(not_in_paste()?) }
keyword! { tdefgens(not_in_paste()?, not_in_bool()?) }
keyword! { tgnames(not_in_paste()?, not_in_bool()?) }
keyword! { twheres(not_in_paste()?, not_in_bool()?) }
use meta::Scope as MS;
keyword! { "tmeta": Xmeta(parse_meta(input, MS::T)?) }
keyword! { "vmeta": Xmeta(parse_meta(input, MS::V)?) }
keyword! { "fmeta": Xmeta(parse_meta(input, MS::F)?) }
keyword! { tattrs(input.parse()?, not_in_paste()?, not_in_bool()?) }
keyword! { vattrs(input.parse()?, not_in_paste()?, not_in_bool()?) }
keyword! { fattrs(input.parse()?, not_in_paste()?, not_in_bool()?) }
keyword! { vtype(
SubstVType::parse(input, kw.span())?,
not_in_paste()?, not_in_bool()?,
) }
keyword! { vpat(
SubstVPat::parse(input, kw.span())?,
not_in_paste()?, not_in_bool()?,
) }
keyword! { tdefvariants(
parse_def_body(
input,
"tdefvariants needs to contain the variant definitions",
)?,
not_in_paste()?, not_in_bool()?,
) }
keyword! { fdefine(
(!input.is_empty()).then(|| input.parse()).transpose()?,
not_in_paste()?, not_in_bool()?
) }
keyword! { vdefbody(
input.parse()?,
parse_def_body(
input,
"vdefbody needs to contain the body definition",
)?,
not_in_paste()?, not_in_bool()?,
) }
keyword! { is_empty(bool_only()?, {
let content;
let _ = parenthesized!(content in input);
content.parse()?
}) }
keyword! { approx_equal(bool_only()?, {
let args =
Punctuated::<_, Token![,]>::parse_separated_nonempty(
&in_parens(input)?,
)?;
if args.len() != 2 {
return Err(kw.error(
"approx_equal() requires two comma-separated arguments"
))
}
let mut args = args.into_iter();
let mut arg = || args.next().unwrap();
[ arg(), arg() ]
}) }
keyword! { paste(Template::parse(input)?, not_in_bool()?) }
keyword! { when(input.parse()?, not_in_bool()?) }
keyword! { define(input.parse()?, not_in_bool()?) }
keyword! { defcond(input.parse()?, not_in_bool()?) }
keyword! { "false": False(bool_only()?) }
keyword! { "true": True(bool_only()?) }
keyword! { "if": If(parse_if(input)?, not_in_bool()?) }
keyword! { select1(parse_if(input)?, not_in_bool()?) }
keyword! { ignore(input.parse()?, not_in_bool()?) }
keyword! { error(input.parse()?, not_in_bool()?) }
keyword! { dbg(input.parse()?) }
keyword! { dbg_all_keywords(not_in_bool()?) }
keyword! { "crate": Crate(not_in_paste()?, not_in_bool()?) }
keyword! { "_dd_intern_crate": Crate(not_in_paste()?, not_in_bool()?) }
keyword! { "for": For(
RepeatedTemplate::parse_for(input)?,
not_in_bool()?,
)}
let any_all_contents = |input: ParseStream<'i>| {
Punctuated::parse_terminated(&in_parens(input)?)
};
keyword! { any(any_all_contents(input)?, bool_only()?) }
keyword! { all(any_all_contents(input)?, bool_only()?) }
keyword! { not(in_parens(input)?.parse()?, bool_only()?) }
if let Ok(case) = kw.to_string().parse() {
return from_sd(SD::ChangeCase(
Template::parse(input)?,
case,
not_in_bool()?,
));
}
if let Ok(user_defined) = kw.clone().try_into() {
return from_sd(SD::UserDefined(user_defined));
}
Err(kw.error("unknown derive-deftly keyword"))
}
}
impl<O: SubstParseContext> Parse for DbgDumpRequest<O> {
fn parse(input: ParseStream) -> syn::Result<Self> {
O::parse_maybe_within_parens(input, |input| {
let note = if input.peek(syn::LitStr) {
let note: syn::LitStr = input.parse()?;
O::parse_maybe_comma(input)?;
Some(note.value())
} else {
None
};
let content_buf;
let content = if O::IS_BOOL {
input
} else {
let _ = braced!(content_buf in input);
&content_buf
};
let content_string = ();
let content_parsed = content.parse()?;
Ok(DbgDumpRequest {
note,
content_string,
content_parsed,
})
})
}
}
impl<O: SubstParseContext> DbgDumpRequest<O> {
pub fn display_heading<'s>(
&'s self,
ctx: &'s Context,
) -> impl Display + 's {
struct Adapter<'a, O: SubstParseContext>(
&'a DbgDumpRequest<O>,
&'a Context<'a>,
);
impl<O: SubstParseContext> Display for Adapter<'_, O> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Adapter(ddr, ctx) = self;
if let Some(note) = &ddr.note {
write!(f, "{:?} ", ¬e)?;
}
write!(f, "{}", ctx.display_for_dbg())
}
}
Adapter(self, ctx)
}
}
impl Parse for ExplicitError {
fn parse(input: ParseStream) -> syn::Result<Self> {
let message: syn::LitStr = input.parse()?;
if message.suffix() != "" {
return Err(message.error("suffix forbidden on error string"));
}
Ok(ExplicitError { message })
}
}
impl<O: SubstParseContext> SubstIf<O> {
fn parse(input: ParseStream, kw_span: Span) -> syn::Result<Self> {
let mut tests = Vec::new();
let mut otherwise = None;
loop {
let condition = input.parse()?;
let content;
let _br = braced![ content in input ];
let consequence = Template::parse(&content)?;
tests.push((condition, consequence));
if input.is_empty() {
break;
}
let lookahead1 = input.lookahead1();
if lookahead1.peek(syn::Ident) {
continue;
} else if lookahead1.peek(Token![else]) {
} else {
return Err(lookahead1.error());
}
let _else: Token![else] = input.parse()?;
let lookahead = input.lookahead1();
if lookahead.peek(Token![if]) {
let _if: Token![if] = input.parse()?;
continue;
} else if lookahead.peek(token::Brace) {
let content;
let _br = braced![ content in input ];
otherwise = Some(Template::parse(&content)?.into());
break;
} else {
return Err(lookahead.error());
}
}
Ok(SubstIf {
kw_span,
tests,
otherwise,
})
}
}
impl SubstVis {
pub fn syn_vis<'c>(
&self,
ctx: &'c Context<'c>,
tspan: Span,
) -> syn::Result<&'c syn::Visibility> {
let field_decl_vis =
|| Ok::<_, syn::Error>(&ctx.field(&tspan)?.field.vis);
Ok(match self {
SubstVis::T => &ctx.top.vis,
SubstVis::FD => field_decl_vis()?,
SubstVis::F => {
let field = field_decl_vis()?;
match ctx.top.data {
syn::Data::Struct(_) | syn::Data::Union(_) => field,
syn::Data::Enum(_) => &ctx.top.vis,
}
}
})
}
}
impl RawAttrEntry {
fn simple(self, _negated: &Token![!]) -> syn::Result<syn::Path> {
Ok(self.path)
}
}
impl Parse for RawAttr {
fn parse(input: ParseStream) -> syn::Result<Self> {
if input.is_empty() {
return Ok(RawAttr::Default);
}
let la = input.lookahead1();
let negated;
if la.peek(Token![!]) {
negated = Some(input.parse()?);
} else if la.peek(Token![=]) {
let _: Token![=] = input.parse()?;
negated = None;
} else {
negated = None;
}
let entries: Punctuated<RawAttrEntry, _> =
input.call(Punctuated::parse_terminated)?;
if let Some(negated) = &negated {
let exclusions = entries
.into_iter()
.map(|ent| ent.simple(negated))
.try_collect()?;
Ok(RawAttr::Exclude { exclusions })
} else {
Ok(RawAttr::Include { entries })
}
}
}
impl Parse for RawAttrEntry {
fn parse(input: ParseStream) -> syn::Result<Self> {
let path = input.parse()?;
Ok(RawAttrEntry { path })
}
}
macro_rules! te_extract_when { { $te:expr } => {
match $te {
TE::Subst(Subst { kw_span, sd: SD::when(bc, _) }) => {
Left((kw_span, bc))
}
other => Right(other),
}
} }
impl<O: SubstParseContext> RepeatedTemplate<O> {
fn parse_in_parens(input: ParseStream) -> syn::Result<TemplateElement<O>> {
let template;
let paren = parenthesized!(template in input);
let rt = RepeatedTemplate::parse(&template, paren.span, None)?;
Ok(TE::Repeat(rt))
}
fn parse_for(input: ParseStream) -> syn::Result<RepeatedTemplate<O>> {
let over: Ident = input.parse()?;
let over = if over == "fields" {
RepeatOver::Fields
} else if over == "variants" {
RepeatOver::Variants
} else {
return Err(
over.error("$for must be followed by 'fields' or 'variants'")
);
};
let template;
let brace = braced!(template in input);
RepeatedTemplate::parse(&template, brace.span, Some(over))
}
fn parse(
input: ParseStream,
span: DelimSpan,
over: Option<RepeatOver>,
) -> Result<RepeatedTemplate<O>, syn::Error> {
use TemplateWithWhens as TWW;
let TWW { elements } =
TWW::parse_special(input, &mut Default::default())?;
let mut elements = VecDeque::from(elements);
let mut whens = vec![];
while let Some(()) = {
match elements.pop_front().map(|e| te_extract_when!(e)) {
Some(Left((_kw_span, bc))) => {
whens.push(bc);
Some(())
}
Some(Right(other)) => {
elements.push_front(other);
None
}
None => None,
}
} {}
let elements = Vec::from(elements);
let template = TemplateWithWhens { elements };
let template = Template::try_from(template)?;
let over = match over {
Some(over) => Ok(over),
None => {
let mut visitor = RepeatAnalysisVisitor::default();
template.analyse_repeat(&mut visitor)?;
visitor.finish(span)
}
};
match over {
Ok(over) => Ok(RepeatedTemplate {
over,
template,
whens,
}),
Err(errs) => Err(errs),
}
}
}
impl<O> TryFrom<TemplateWithWhens<O>> for Template<O>
where
O: SubstParseContext,
{
type Error = syn::Error;
fn try_from(unchecked: TemplateWithWhens<O>) -> syn::Result<Template<O>> {
let TemplateWithWhens { elements } = unchecked;
for e in &elements {
if let Left((kw_span, _)) = te_extract_when!(e) {
return Err(kw_span.error(
"${when } must be at the top-level of a repetition, before other content"
));
}
}
Ok(Template { elements })
}
}
pub fn preprocess_attrs(
attrs: &[syn::Attribute],
) -> syn::Result<meta::PreprocessedMetas> {
attrs
.iter()
.filter_map(|attr| {
match attr.style {
syn::AttrStyle::Outer => {}
syn::AttrStyle::Inner(_) => return None,
};
if attr.path().leading_colon.is_some() {
return None;
}
let segment = attr.path().segments.iter().exactly_one().ok()?;
if segment.ident != "deftly" {
return None;
}
Some(attr)
})
.map(|attr| {
attr.call_in_parens(meta::PreprocessedValueList::parse_inner)
})
.collect()
}
pub fn preprocess_fields(
fields: &syn::Fields,
) -> syn::Result<Vec<PreprocessedField>> {
let fields = match fields {
syn::Fields::Named(f) => &f.named,
syn::Fields::Unnamed(f) => &f.unnamed,
syn::Fields::Unit => return Ok(vec![]),
};
fields
.into_iter()
.map(|field| {
let pmetas = preprocess_attrs(&field.attrs)?;
Ok(PreprocessedField { pmetas })
})
.collect()
}