use super::framework::*;
#[derive(Debug)]
pub struct BooleanContext(Void);
struct Found;
fn is_found(r: Result<(), Found>) -> bool {
r.is_err()
}
impl SubstParseContext for BooleanContext {
type NotInPaste = ();
type NotInBool = Void;
type BoolOnly = ();
fn not_in_paste(_: &impl Spanned) -> syn::Result<()> {
Ok(())
}
fn bool_only(_: &impl Spanned) -> syn::Result<()> {
Ok(())
}
fn not_in_bool(span: &impl Spanned) -> syn::Result<Void> {
Err(span.error(
"derive-adhoc construct is an expansion - not valid in a condition",
))
}
type SpecialParseContext = ();
}
impl Subst<BooleanContext> {
pub fn eval_bool(&self, ctx: &Context) -> syn::Result<bool> {
let v_fields = || ctx.variant(&self.kw_span).map(|v| &v.fields);
use syn::Fields as SF;
let r = match &self.sd {
SD::is_enum(..) => ctx.is_enum(),
SD::is_struct(..) => matches!(ctx.top.data, syn::Data::Struct(_)),
SD::is_union(..) => matches!(ctx.top.data, syn::Data::Union(_)),
SD::v_is_unit(..) => matches!(v_fields()?, SF::Unit),
SD::v_is_tuple(..) => matches!(v_fields()?, SF::Unnamed(..)),
SD::v_is_named(..) => matches!(v_fields()?, SF::Named(..)),
SD::xmeta(sm) => {
let SubstMeta {
path,
as_,
level: _,
} = sm;
use SubstMetaAs as SMA;
if let Some(as_) = as_ {
match as_ {
SMA::str(nb) | SMA::tokens(nb, ..) | SMA::ty(nb) => {
void::unreachable(*nb)
}
}
};
is_found(path.search_eval_bool(sm.pmetas(ctx)?))
}
SD::approx_equal(_, [a, b]) => {
let s = |x: &Template<_>| {
let mut out = TokenAccumulator::new();
x.expand(ctx, &mut out);
let out = out.tokens()?;
Ok::<TokenStream, syn::Error>(out)
};
tokens_cmp(s(a)?, s(b)?) == Ordering::Equal
}
SD::UserDefined(name) => name.lookup_eval_bool(ctx)?,
SD::False(..) => false,
SD::True(..) => true,
SD::not(v, _) => !v.eval_bool(ctx)?,
SD::any(vs, _) => vs
.iter()
.find_map(|v| match v.eval_bool(ctx) {
Ok(true) => Some(Ok(true)),
Err(e) => Some(Err(e)),
Ok(false) => None,
})
.unwrap_or(Ok(false))?,
SD::all(vs, _) => vs
.iter()
.find_map(|v| match v.eval_bool(ctx) {
Ok(true) => None,
Err(e) => Some(Err(e)),
Ok(false) => Some(Ok(false)),
})
.unwrap_or(Ok(true))?,
SD::Vis(vis, _) => match vis.syn_vis(ctx, self.kw_span)? {
syn::Visibility::Public(_) => true,
_ => false,
},
SD::tname(not_in_bool)
| SD::ttype(not_in_bool)
| SD::tdeftype(not_in_bool)
| SD::vname(not_in_bool)
| SD::fname(not_in_bool)
| SD::ftype(not_in_bool)
| SD::vtype(_, _, not_in_bool)
| SD::tdefkwd(not_in_bool)
| SD::tattrs(_, _, not_in_bool)
| SD::vattrs(_, _, not_in_bool)
| SD::fattrs(_, _, not_in_bool)
| SD::tgens(_, not_in_bool)
| SD::tdefgens(_, not_in_bool)
| SD::tgnames(_, not_in_bool)
| SD::twheres(_, not_in_bool)
| SD::vpat(_, _, not_in_bool)
| SD::fpatname(not_in_bool)
| SD::tdefvariants(_, _, not_in_bool)
| SD::fdefine(_, _, not_in_bool)
| SD::vdefbody(_, _, _, not_in_bool)
| SD::paste(_, not_in_bool)
| SD::ChangeCase(_, _, not_in_bool)
| SD::when(_, not_in_bool)
| SD::define(_, not_in_bool)
| SD::defcond(_, not_in_bool)
| SD::For(_, not_in_bool)
| SD::If(_, not_in_bool)
| SD::select1(_, not_in_bool)
| SD::dbg_all_keywords(not_in_bool)
| SD::Crate(_, not_in_bool) => void::unreachable(*not_in_bool),
};
Ok(r)
}
}
impl DefinitionName {
fn lookup_eval_bool(&self, ctx: &Context<'_>) -> syn::Result<bool> {
let (def, ctx) = ctx.find_definition::<DefCondBody>(self)?.ok_or_else(|| {
let mut error = self.error("user-defined condition not fund");
if let Some(def) = ctx.definitions.find_raw::<DefinitionBody>(self) {
error.combine(
def.name.error(
"this user-defined expansion used as a condition (perhaps you meant ${defcond ?}"
)
);
}
error
})?;
def.body.eval_bool(&ctx)
}
}
impl SubstMetaPath {
fn search_eval_bool(
&self,
pmetas: &PreprocessedMetas,
) -> Result<(), Found> {
self.search(
pmetas,
&mut |_av| Err(Found),
&mut |nearby| match nearby.kind {
FNMNK::List => Err(Found), FNMNK::Unit => Ok(()),
FNMNK::Lit => Ok(()),
},
)
}
}
pub fn tokens_cmp(a: TokenStream, b: TokenStream) -> cmp::Ordering {
use proc_macro2::Group;
fn tt_cmp(a: TokenTree, b: TokenTree) -> Ordering {
let discrim = |tt: &_| match tt {
TT::Punct(_) => 0,
TT::Literal(_) => 1,
TT::Ident(_) => 2,
TT::Group(_) => 3,
};
discrim(&a).cmp(&discrim(&b)).then_with(|| match (a, b) {
(TT::Group(a), TT::Group(b)) => group_cmp(a, b),
(l, r) => l.to_string().cmp(&r.to_string()),
})
}
fn group_cmp(a: Group, b: Group) -> Ordering {
let delim = |g: &Group| {
proc_macro2::Group::new(g.delimiter(), TokenStream::new())
.to_string()
};
delim(&a)
.cmp(&delim(&b))
.then_with(|| tokens_cmp(a.stream(), b.stream()))
}
for (a, b) in izip!(a, b) {
match tt_cmp(a, b) {
Ordering::Equal => {}
neq => return neq,
}
}
return Ordering::Equal;
}