1use super::framework::*;
4
5#[derive(Debug)]
13pub struct BooleanContext(Void);
14
15pub struct Found;
16
17fn is_found(r: Result<(), Found>) -> bool {
18 r.is_err()
19}
20
21impl SubstParseContext for BooleanContext {
22 type NotInPaste = ();
23 type NotInConcat = ();
24 type NotInBool = Void;
25 type ConcatOnly = Void;
26 type BoolOnly = ();
27 const IS_BOOL: bool = true;
28 type DbgContent = Subst<BooleanContext>;
29
30 fn not_in_paste(_: &impl Spanned) -> syn::Result<()> {
31 Ok(())
32 }
33 fn not_in_concat(_: &impl Spanned) -> syn::Result<()> {
34 Ok(())
35 }
36 fn bool_only(_: &impl Spanned) -> syn::Result<()> {
37 Ok(())
38 }
39 fn meta_recog_usage(_: &meta::SubstMeta<Self>) -> meta::Usage {
40 meta::Usage::BOOL_ONLY
41 }
42
43 fn not_in_bool(span: &impl Spanned) -> syn::Result<Void> {
44 Err(span.error(
45 "derive-deftly construct is an expansion - not valid in a condition",
46 ))
47 }
48
49 type SpecialParseContext = ();
50
51 fn missing_keyword_arguments(kw_span: Span) -> syn::Result<Void> {
52 Err(kw_span.error("missing parameters to condition"))
53 }
54}
55
56impl Subst<BooleanContext> {
57 pub fn eval_bool<'c>(
58 &self,
59 gctx: GeneralContext<'c>,
60 ) -> syn::Result<bool> {
61 let kw_span = self.kw_span;
63
64 let ctx = gctx.full_ctx(kw_span);
65
66 let v_fields = || ctx?.variant(&kw_span).map(|v| &v.fields);
67 use syn::Fields as SF;
68
69 let expand = |x: &Template<_>| {
70 let mut out = TokenAccumulator::new();
71 x.expand(gctx, &mut out);
72 let out = out.tokens()?;
73 Ok::<TokenStream, syn::Error>(out)
74 };
75
76 let r = match &self.sd {
77 SD::is_enum(..) => ctx?.is_enum(),
78 SD::is_struct(..) => matches!(ctx?.top.data, syn::Data::Struct(_)),
79 SD::is_union(..) => matches!(ctx?.top.data, syn::Data::Union(_)),
80 SD::v_is_unit(..) => matches!(v_fields()?, SF::Unit),
81 SD::v_is_tuple(..) => matches!(v_fields()?, SF::Unnamed(..)),
82 SD::v_is_named(..) => matches!(v_fields()?, SF::Named(..)),
83 SD::tgens(_) => !ctx?.top.generics.params.is_empty(),
84
85 SD::Xmeta(sm) => {
86 let meta::SubstMeta {
87 desig: meta::Desig { label, scope: _ },
88 as_,
89 default,
90 } = sm;
91 match default {
92 None => {}
93 Some((_, nb, _)) => void::unreachable(*nb),
94 };
95 use meta::SubstAs as mSA;
96 if let Some(as_) = as_ {
97 match as_ {
98 mSA::expr(nb, ..)
99 | mSA::ident(nb, ..)
100 | mSA::items(nb, ..)
101 | mSA::path(nb)
102 | mSA::str(nb)
103 | mSA::token_stream(nb, ..)
104 | mSA::ty(nb) => void::unreachable(*nb),
105 }
106 };
107 is_found(label.search_eval_bool(sm.pmetas(ctx?, kw_span)?))
108 }
109
110 SD::is_empty(_, content) => expand(content)?.is_empty(),
111 SD::approx_equal(_, [a, b]) => {
112 tokens_cmpeq(expand(a)?, expand(b)?, kw_span)?
113 == Equality::Equal
114 }
115
116 SD::UserDefined(name) => name.lookup_eval_bool(gctx)?,
117
118 SD::False(..) => false,
119 SD::True(..) => true,
120
121 SD::not(v, _) => !v.eval_bool(gctx)?,
122 SD::any(vs, _) => vs
123 .iter()
124 .find_map(|v| match v.eval_bool(gctx) {
125 Ok(true) => Some(Ok(true)),
126 Err(e) => Some(Err(e)),
127 Ok(false) => None,
128 })
129 .unwrap_or(Ok(false))?,
130 SD::all(vs, _) => vs
131 .iter()
132 .find_map(|v| match v.eval_bool(gctx) {
133 Ok(true) => None,
134 Err(e) => Some(Err(e)),
135 Ok(false) => Some(Ok(false)),
136 })
137 .unwrap_or(Ok(true))?,
138
139 SD::Vis(vis, _) => match vis.syn_vis(ctx?, kw_span)? {
140 syn::Visibility::Public(_) => true,
141 _ => false,
142 },
143
144 SD::dbg(ddr) => {
145 let r = ddr.content_parsed.eval_bool(gctx);
146 let () = &ddr.content_string;
147 let w = |s: fmt::Arguments| {
148 eprintln!(
149 "derive-deftly dbg condition {} evaluated to {}",
150 ddr.display_heading(gctx),
151 s,
152 )
153 };
154 match &r {
155 Ok(y) => w(format_args!("{:?}", y)),
156 Err(e) => w(format_args!("error: {}", e)),
157 };
158 r?
159 }
160
161 SD::tname(not_in_bool)
163 | SD::ttype(not_in_bool)
164 | SD::tdeftype(not_in_bool)
165 | SD::vname(not_in_bool)
166 | SD::fname(not_in_bool)
167 | SD::ftype(not_in_bool)
168 | SD::vtype(_, _, not_in_bool)
169 | SD::tdefkwd(not_in_bool)
170 | SD::vindex(not_in_bool, ..)
171 | SD::findex(not_in_bool, ..)
172 | SD::tattrs(_, _, not_in_bool)
173 | SD::vattrs(_, _, not_in_bool)
174 | SD::fattrs(_, _, not_in_bool)
175 | SD::tdefgens(_, not_in_bool)
176 | SD::tgnames(_, not_in_bool)
177 | SD::twheres(_, not_in_bool)
178 | SD::vpat(_, _, not_in_bool)
179 | SD::fpatname(not_in_bool)
180 | SD::tdefvariants(_, _, not_in_bool)
181 | SD::fdefine(_, _, not_in_bool)
182 | SD::vdefbody(_, _, _, not_in_bool)
183 | SD::paste(_, not_in_bool)
184 | SD::paste_spanned(_, _, not_in_bool, ..)
185 | SD::ChangeCase(.., paste::ChangeCaseOkIn { not_in_bool, .. })
186 | SD::concat(_, not_in_bool, ..)
187 | SD::when(_, not_in_bool)
188 | SD::define(_, not_in_bool)
189 | SD::defcond(_, not_in_bool)
190 | SD::For(_, not_in_bool)
191 | SD::If(_, not_in_bool)
192 | SD::select1(_, not_in_bool)
193 | SD::ignore(_, not_in_bool)
194 | SD::error(_, not_in_bool)
195 | SD::require_beta(_, not_in_bool)
196 | SD::dbg_all_keywords(not_in_bool)
197 | SD::Crate(_, not_in_bool) => void::unreachable(*not_in_bool),
198 };
199 Ok(r)
200 }
201}
202
203impl DefinitionName {
204 fn lookup_eval_bool<'c>(
205 &self,
206 ctx: GeneralContext<'c>,
207 ) -> syn::Result<bool> {
208 let (def, ctx) = ctx.find_definition::<DefCondBody>(self)?
209 .ok_or_else(|| {
210 let mut error = self.error(format!(
211 "user-defined condition `{}` not found",
212 self,
213 ));
214 if let Some(def) = ctx.defs().defs
215 .find_raw::<DefinitionBody>(self)
216 {
217 error.combine(
220 def.name.error(
221"this user-defined expansion used as a condition (perhaps you meant ${defcond ?}"
222 )
223 );
224 }
225 error
226 })?;
227
228 def.body.eval_bool(ctx.as_ref())
229 }
230}