Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/derive/src/container_attributes.rs
6599 views
1
//! Contains code related to container attributes for reflected types.
2
//!
3
//! A container attribute is an attribute which applies to an entire struct or enum
4
//! as opposed to a particular field or variant. An example of such an attribute is
5
//! the derive helper attribute for `Reflect`, which looks like:
6
//! `#[reflect(PartialEq, Default, ...)]`.
7
8
use crate::{
9
attribute_parser::terminated_parser, custom_attributes::CustomAttributes,
10
derive_data::ReflectTraitToImpl,
11
};
12
use bevy_macro_utils::fq_std::{FQAny, FQClone, FQOption, FQResult};
13
use proc_macro2::{Ident, Span};
14
use quote::quote_spanned;
15
use syn::{
16
ext::IdentExt, parenthesized, parse::ParseStream, spanned::Spanned, token, Expr, LitBool,
17
MetaList, MetaNameValue, Path, Token, WhereClause,
18
};
19
20
mod kw {
21
syn::custom_keyword!(from_reflect);
22
syn::custom_keyword!(type_path);
23
syn::custom_keyword!(Debug);
24
syn::custom_keyword!(PartialEq);
25
syn::custom_keyword!(Hash);
26
syn::custom_keyword!(Clone);
27
syn::custom_keyword!(no_field_bounds);
28
syn::custom_keyword!(no_auto_register);
29
syn::custom_keyword!(opaque);
30
}
31
32
// The "special" trait idents that are used internally for reflection.
33
// Received via attributes like `#[reflect(PartialEq, Hash, ...)]`
34
const DEBUG_ATTR: &str = "Debug";
35
const PARTIAL_EQ_ATTR: &str = "PartialEq";
36
const HASH_ATTR: &str = "Hash";
37
38
// The traits listed below are not considered "special" (i.e. they use the `ReflectMyTrait` syntax)
39
// but useful to know exist nonetheless
40
pub(crate) const REFLECT_DEFAULT: &str = "ReflectDefault";
41
42
// Attributes for `FromReflect` implementation
43
const FROM_REFLECT_ATTR: &str = "from_reflect";
44
45
// Attributes for `TypePath` implementation
46
const TYPE_PATH_ATTR: &str = "type_path";
47
48
// The error message to show when a trait/type is specified multiple times
49
const CONFLICTING_TYPE_DATA_MESSAGE: &str = "conflicting type data registration";
50
51
/// A marker for trait implementations registered via the `Reflect` derive macro.
52
#[derive(Clone, Default)]
53
pub(crate) enum TraitImpl {
54
/// The trait is not registered as implemented.
55
#[default]
56
NotImplemented,
57
58
/// The trait is registered as implemented.
59
Implemented(Span),
60
61
/// The trait is registered with a custom function rather than an actual implementation.
62
Custom(Path, Span),
63
}
64
65
impl TraitImpl {
66
/// Merges this [`TraitImpl`] with another.
67
///
68
/// Update `self` with whichever value is not [`TraitImpl::NotImplemented`].
69
/// If `other` is [`TraitImpl::NotImplemented`], then `self` is not modified.
70
/// An error is returned if neither value is [`TraitImpl::NotImplemented`].
71
pub fn merge(&mut self, other: TraitImpl) -> Result<(), syn::Error> {
72
match (&self, other) {
73
(TraitImpl::NotImplemented, value) => {
74
*self = value;
75
Ok(())
76
}
77
(_, TraitImpl::NotImplemented) => Ok(()),
78
(_, TraitImpl::Implemented(span) | TraitImpl::Custom(_, span)) => {
79
Err(syn::Error::new(span, CONFLICTING_TYPE_DATA_MESSAGE))
80
}
81
}
82
}
83
}
84
85
/// A collection of attributes used for deriving `FromReflect`.
86
#[derive(Clone, Default)]
87
pub(crate) struct FromReflectAttrs {
88
auto_derive: Option<LitBool>,
89
}
90
91
impl FromReflectAttrs {
92
/// Returns true if `FromReflect` should be automatically derived as part of the `Reflect` derive.
93
pub fn should_auto_derive(&self) -> bool {
94
self.auto_derive.as_ref().is_none_or(LitBool::value)
95
}
96
}
97
98
/// A collection of attributes used for deriving `TypePath` via the `Reflect` derive.
99
///
100
/// Note that this differs from the attributes used by the `TypePath` derive itself,
101
/// which look like `[type_path = "my_crate::foo"]`.
102
/// The attributes used by reflection take the form `#[reflect(type_path = false)]`.
103
///
104
/// These attributes should only be used for `TypePath` configuration specific to
105
/// deriving `Reflect`.
106
#[derive(Clone, Default)]
107
pub(crate) struct TypePathAttrs {
108
auto_derive: Option<LitBool>,
109
}
110
111
impl TypePathAttrs {
112
/// Returns true if `TypePath` should be automatically derived as part of the `Reflect` derive.
113
pub fn should_auto_derive(&self) -> bool {
114
self.auto_derive.as_ref().is_none_or(LitBool::value)
115
}
116
}
117
118
/// A collection of traits that have been registered for a reflected type.
119
///
120
/// This keeps track of a few traits that are utilized internally for reflection
121
/// (we'll call these traits _special traits_ within this context), but it
122
/// will also keep track of all registered traits. Traits are registered as part of the
123
/// `Reflect` derive macro using the helper attribute: `#[reflect(...)]`.
124
///
125
/// The list of special traits are as follows:
126
/// * `Debug`
127
/// * `Hash`
128
/// * `PartialEq`
129
///
130
/// When registering a trait, there are a few things to keep in mind:
131
/// * Traits must have a valid `Reflect{}` struct in scope. For example, `Default`
132
/// needs `bevy_reflect::prelude::ReflectDefault` in scope.
133
/// * Traits must be single path identifiers. This means you _must_ use `Default`
134
/// instead of `std::default::Default` (otherwise it will try to register `Reflectstd`!)
135
/// * A custom function may be supplied in place of an actual implementation
136
/// for the special traits (but still follows the same single-path identifier
137
/// rules as normal).
138
///
139
/// # Example
140
///
141
/// Registering the `Default` implementation:
142
///
143
/// ```ignore (bevy_reflect is not accessible from this crate)
144
/// // Import ReflectDefault so it's accessible by the derive macro
145
/// use bevy_reflect::prelude::ReflectDefault;
146
///
147
/// #[derive(Reflect, Default)]
148
/// #[reflect(Default)]
149
/// struct Foo;
150
/// ```
151
///
152
/// Registering the `Hash` implementation:
153
///
154
/// ```ignore (bevy_reflect is not accessible from this crate)
155
/// // `Hash` is a "special trait" and does not need (nor have) a ReflectHash struct
156
///
157
/// #[derive(Reflect, Hash)]
158
/// #[reflect(Hash)]
159
/// struct Foo;
160
/// ```
161
///
162
/// Registering the `Hash` implementation using a custom function:
163
///
164
/// ```ignore (bevy_reflect is not accessible from this crate)
165
/// // This function acts as our `Hash` implementation and
166
/// // corresponds to the `Reflect::reflect_hash` method.
167
/// fn get_hash(foo: &Foo) -> Option<u64> {
168
/// Some(123)
169
/// }
170
///
171
/// #[derive(Reflect)]
172
/// // Register the custom `Hash` function
173
/// #[reflect(Hash(get_hash))]
174
/// struct Foo;
175
/// ```
176
///
177
/// > __Note:__ Registering a custom function only works for special traits.
178
#[derive(Default, Clone)]
179
pub(crate) struct ContainerAttributes {
180
clone: TraitImpl,
181
debug: TraitImpl,
182
hash: TraitImpl,
183
partial_eq: TraitImpl,
184
from_reflect_attrs: FromReflectAttrs,
185
type_path_attrs: TypePathAttrs,
186
custom_where: Option<WhereClause>,
187
no_field_bounds: bool,
188
no_auto_register: bool,
189
custom_attributes: CustomAttributes,
190
is_opaque: bool,
191
idents: Vec<Ident>,
192
}
193
194
impl ContainerAttributes {
195
/// Parse a comma-separated list of container attributes.
196
///
197
/// # Example
198
/// - `Hash, Debug(custom_debug), MyTrait`
199
pub fn parse_terminated(
200
&mut self,
201
input: ParseStream,
202
trait_: ReflectTraitToImpl,
203
) -> syn::Result<()> {
204
terminated_parser(Token![,], |stream| {
205
self.parse_container_attribute(stream, trait_)
206
})(input)?;
207
208
Ok(())
209
}
210
211
/// Parse the contents of a `#[reflect(...)]` attribute into a [`ContainerAttributes`] instance.
212
///
213
/// # Example
214
/// - `#[reflect(Hash, Debug(custom_debug), MyTrait)]`
215
/// - `#[reflect(no_field_bounds)]`
216
pub fn parse_meta_list(
217
&mut self,
218
meta: &MetaList,
219
trait_: ReflectTraitToImpl,
220
) -> syn::Result<()> {
221
meta.parse_args_with(|stream: ParseStream| self.parse_terminated(stream, trait_))
222
}
223
224
/// Parse a single container attribute.
225
fn parse_container_attribute(
226
&mut self,
227
input: ParseStream,
228
trait_: ReflectTraitToImpl,
229
) -> syn::Result<()> {
230
let lookahead = input.lookahead1();
231
if lookahead.peek(Token![@]) {
232
self.custom_attributes.parse_custom_attribute(input)
233
} else if lookahead.peek(Token![where]) {
234
self.parse_custom_where(input)
235
} else if lookahead.peek(kw::from_reflect) {
236
self.parse_from_reflect(input, trait_)
237
} else if lookahead.peek(kw::type_path) {
238
self.parse_type_path(input, trait_)
239
} else if lookahead.peek(kw::opaque) {
240
self.parse_opaque(input)
241
} else if lookahead.peek(kw::no_field_bounds) {
242
self.parse_no_field_bounds(input)
243
} else if lookahead.peek(kw::Clone) {
244
self.parse_clone(input)
245
} else if lookahead.peek(kw::no_auto_register) {
246
self.parse_no_auto_register(input)
247
} else if lookahead.peek(kw::Debug) {
248
self.parse_debug(input)
249
} else if lookahead.peek(kw::Hash) {
250
self.parse_hash(input)
251
} else if lookahead.peek(kw::PartialEq) {
252
self.parse_partial_eq(input)
253
} else if lookahead.peek(Ident::peek_any) {
254
self.parse_ident(input)
255
} else {
256
Err(lookahead.error())
257
}
258
}
259
260
/// Parse an ident (for registration).
261
///
262
/// Examples:
263
/// - `#[reflect(MyTrait)]` (registers `ReflectMyTrait`)
264
fn parse_ident(&mut self, input: ParseStream) -> syn::Result<()> {
265
let ident = input.parse::<Ident>()?;
266
267
if input.peek(token::Paren) {
268
return Err(syn::Error::new(ident.span(), format!(
269
"only [{DEBUG_ATTR:?}, {PARTIAL_EQ_ATTR:?}, {HASH_ATTR:?}] may specify custom functions",
270
)));
271
}
272
273
let ident_name = ident.to_string();
274
275
// Create the reflect ident
276
let mut reflect_ident = crate::ident::get_reflect_ident(&ident_name);
277
// We set the span to the old ident so any compile errors point to that ident instead
278
reflect_ident.set_span(ident.span());
279
280
add_unique_ident(&mut self.idents, reflect_ident)?;
281
282
Ok(())
283
}
284
285
/// Parse `clone` attribute.
286
///
287
/// Examples:
288
/// - `#[reflect(Clone)]`
289
/// - `#[reflect(Clone(custom_clone_fn))]`
290
fn parse_clone(&mut self, input: ParseStream) -> syn::Result<()> {
291
let ident = input.parse::<kw::Clone>()?;
292
293
if input.peek(token::Paren) {
294
let content;
295
parenthesized!(content in input);
296
let path = content.parse::<Path>()?;
297
self.clone.merge(TraitImpl::Custom(path, ident.span))?;
298
} else {
299
self.clone = TraitImpl::Implemented(ident.span);
300
}
301
302
Ok(())
303
}
304
305
/// Parse special `Debug` registration.
306
///
307
/// Examples:
308
/// - `#[reflect(Debug)]`
309
/// - `#[reflect(Debug(custom_debug_fn))]`
310
fn parse_debug(&mut self, input: ParseStream) -> syn::Result<()> {
311
let ident = input.parse::<kw::Debug>()?;
312
313
if input.peek(token::Paren) {
314
let content;
315
parenthesized!(content in input);
316
let path = content.parse::<Path>()?;
317
self.debug.merge(TraitImpl::Custom(path, ident.span))?;
318
} else {
319
self.debug = TraitImpl::Implemented(ident.span);
320
}
321
322
Ok(())
323
}
324
325
/// Parse special `PartialEq` registration.
326
///
327
/// Examples:
328
/// - `#[reflect(PartialEq)]`
329
/// - `#[reflect(PartialEq(custom_partial_eq_fn))]`
330
fn parse_partial_eq(&mut self, input: ParseStream) -> syn::Result<()> {
331
let ident = input.parse::<kw::PartialEq>()?;
332
333
if input.peek(token::Paren) {
334
let content;
335
parenthesized!(content in input);
336
let path = content.parse::<Path>()?;
337
self.partial_eq.merge(TraitImpl::Custom(path, ident.span))?;
338
} else {
339
self.partial_eq = TraitImpl::Implemented(ident.span);
340
}
341
342
Ok(())
343
}
344
345
/// Parse special `Hash` registration.
346
///
347
/// Examples:
348
/// - `#[reflect(Hash)]`
349
/// - `#[reflect(Hash(custom_hash_fn))]`
350
fn parse_hash(&mut self, input: ParseStream) -> syn::Result<()> {
351
let ident = input.parse::<kw::Hash>()?;
352
353
if input.peek(token::Paren) {
354
let content;
355
parenthesized!(content in input);
356
let path = content.parse::<Path>()?;
357
self.hash.merge(TraitImpl::Custom(path, ident.span))?;
358
} else {
359
self.hash = TraitImpl::Implemented(ident.span);
360
}
361
362
Ok(())
363
}
364
365
/// Parse `opaque` attribute.
366
///
367
/// Examples:
368
/// - `#[reflect(opaque)]`
369
fn parse_opaque(&mut self, input: ParseStream) -> syn::Result<()> {
370
input.parse::<kw::opaque>()?;
371
self.is_opaque = true;
372
Ok(())
373
}
374
375
/// Parse `no_field_bounds` attribute.
376
///
377
/// Examples:
378
/// - `#[reflect(no_field_bounds)]`
379
fn parse_no_field_bounds(&mut self, input: ParseStream) -> syn::Result<()> {
380
input.parse::<kw::no_field_bounds>()?;
381
self.no_field_bounds = true;
382
Ok(())
383
}
384
385
/// Parse `no_auto_register` attribute.
386
///
387
/// Examples:
388
/// - `#[reflect(no_auto_register)]`
389
fn parse_no_auto_register(&mut self, input: ParseStream) -> syn::Result<()> {
390
input.parse::<kw::no_auto_register>()?;
391
self.no_auto_register = true;
392
Ok(())
393
}
394
395
/// Parse `where` attribute.
396
///
397
/// Examples:
398
/// - `#[reflect(where T: Debug)]`
399
fn parse_custom_where(&mut self, input: ParseStream) -> syn::Result<()> {
400
self.custom_where = Some(input.parse()?);
401
Ok(())
402
}
403
404
/// Parse `from_reflect` attribute.
405
///
406
/// Examples:
407
/// - `#[reflect(from_reflect = false)]`
408
fn parse_from_reflect(
409
&mut self,
410
input: ParseStream,
411
trait_: ReflectTraitToImpl,
412
) -> syn::Result<()> {
413
let pair = input.parse::<MetaNameValue>()?;
414
let extracted_bool = extract_bool(&pair.value, |lit| {
415
// Override `lit` if this is a `FromReflect` derive.
416
// This typically means a user is opting out of the default implementation
417
// from the `Reflect` derive and using the `FromReflect` derive directly instead.
418
if trait_ == ReflectTraitToImpl::FromReflect {
419
LitBool::new(true, Span::call_site())
420
} else {
421
lit.clone()
422
}
423
})?;
424
425
if let Some(existing) = &self.from_reflect_attrs.auto_derive {
426
if existing.value() != extracted_bool.value() {
427
return Err(syn::Error::new(
428
extracted_bool.span(),
429
format!("`{FROM_REFLECT_ATTR}` already set to {}", existing.value()),
430
));
431
}
432
} else {
433
self.from_reflect_attrs.auto_derive = Some(extracted_bool);
434
}
435
436
Ok(())
437
}
438
439
/// Parse `type_path` attribute.
440
///
441
/// Examples:
442
/// - `#[reflect(type_path = false)]`
443
fn parse_type_path(
444
&mut self,
445
input: ParseStream,
446
trait_: ReflectTraitToImpl,
447
) -> syn::Result<()> {
448
let pair = input.parse::<MetaNameValue>()?;
449
let extracted_bool = extract_bool(&pair.value, |lit| {
450
// Override `lit` if this is a `FromReflect` derive.
451
// This typically means a user is opting out of the default implementation
452
// from the `Reflect` derive and using the `FromReflect` derive directly instead.
453
if trait_ == ReflectTraitToImpl::TypePath {
454
LitBool::new(true, Span::call_site())
455
} else {
456
lit.clone()
457
}
458
})?;
459
460
if let Some(existing) = &self.type_path_attrs.auto_derive {
461
if existing.value() != extracted_bool.value() {
462
return Err(syn::Error::new(
463
extracted_bool.span(),
464
format!("`{TYPE_PATH_ATTR}` already set to {}", existing.value()),
465
));
466
}
467
} else {
468
self.type_path_attrs.auto_derive = Some(extracted_bool);
469
}
470
471
Ok(())
472
}
473
474
/// Returns true if the given reflected trait name (i.e. `ReflectDefault` for `Default`)
475
/// is registered for this type.
476
pub fn contains(&self, name: &str) -> bool {
477
self.idents.iter().any(|ident| ident == name)
478
}
479
480
/// The list of reflected traits by their reflected ident (i.e. `ReflectDefault` for `Default`).
481
pub fn idents(&self) -> &[Ident] {
482
&self.idents
483
}
484
485
/// The `FromReflect` configuration found within `#[reflect(...)]` attributes on this type.
486
#[expect(
487
clippy::wrong_self_convention,
488
reason = "Method returns `FromReflectAttrs`, does not actually convert data."
489
)]
490
pub fn from_reflect_attrs(&self) -> &FromReflectAttrs {
491
&self.from_reflect_attrs
492
}
493
494
/// The `TypePath` configuration found within `#[reflect(...)]` attributes on this type.
495
pub fn type_path_attrs(&self) -> &TypePathAttrs {
496
&self.type_path_attrs
497
}
498
499
/// Returns the implementation of `PartialReflect::reflect_hash` as a `TokenStream`.
500
///
501
/// If `Hash` was not registered, returns `None`.
502
pub fn get_hash_impl(&self, bevy_reflect_path: &Path) -> Option<proc_macro2::TokenStream> {
503
match &self.hash {
504
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
505
fn reflect_hash(&self) -> #FQOption<u64> {
506
use ::core::hash::{Hash, Hasher};
507
let mut hasher = #bevy_reflect_path::utility::reflect_hasher();
508
Hash::hash(&#FQAny::type_id(self), &mut hasher);
509
Hash::hash(self, &mut hasher);
510
#FQOption::Some(Hasher::finish(&hasher))
511
}
512
}),
513
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
514
fn reflect_hash(&self) -> #FQOption<u64> {
515
#FQOption::Some(#impl_fn(self))
516
}
517
}),
518
TraitImpl::NotImplemented => None,
519
}
520
}
521
522
/// Returns the implementation of `PartialReflect::reflect_partial_eq` as a `TokenStream`.
523
///
524
/// If `PartialEq` was not registered, returns `None`.
525
pub fn get_partial_eq_impl(
526
&self,
527
bevy_reflect_path: &Path,
528
) -> Option<proc_macro2::TokenStream> {
529
match &self.partial_eq {
530
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
531
fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<bool> {
532
let value = <dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<Self>(value);
533
if let #FQOption::Some(value) = value {
534
#FQOption::Some(::core::cmp::PartialEq::eq(self, value))
535
} else {
536
#FQOption::Some(false)
537
}
538
}
539
}),
540
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
541
fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<bool> {
542
#FQOption::Some(#impl_fn(self, value))
543
}
544
}),
545
TraitImpl::NotImplemented => None,
546
}
547
}
548
549
/// Returns the implementation of `PartialReflect::debug` as a `TokenStream`.
550
///
551
/// If `Debug` was not registered, returns `None`.
552
pub fn get_debug_impl(&self) -> Option<proc_macro2::TokenStream> {
553
match &self.debug {
554
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
555
fn debug(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
556
::core::fmt::Debug::fmt(self, f)
557
}
558
}),
559
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
560
fn debug(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
561
#impl_fn(self, f)
562
}
563
}),
564
TraitImpl::NotImplemented => None,
565
}
566
}
567
568
pub fn get_clone_impl(&self, bevy_reflect_path: &Path) -> Option<proc_macro2::TokenStream> {
569
match &self.clone {
570
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
571
#[inline]
572
fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {
573
#FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#FQClone::clone(self)))
574
}
575
}),
576
&TraitImpl::Custom(ref impl_fn, span) => Some(quote_spanned! {span=>
577
#[inline]
578
fn reflect_clone(&self) -> #FQResult<#bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::Reflect>, #bevy_reflect_path::ReflectCloneError> {
579
#FQResult::Ok(#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#impl_fn(self)))
580
}
581
}),
582
TraitImpl::NotImplemented => None,
583
}
584
}
585
586
pub fn custom_attributes(&self) -> &CustomAttributes {
587
&self.custom_attributes
588
}
589
590
/// The custom where configuration found within `#[reflect(...)]` attributes on this type.
591
pub fn custom_where(&self) -> Option<&WhereClause> {
592
self.custom_where.as_ref()
593
}
594
595
/// Returns true if the `no_field_bounds` attribute was found on this type.
596
pub fn no_field_bounds(&self) -> bool {
597
self.no_field_bounds
598
}
599
600
/// Returns true if the `no_auto_register` attribute was found on this type.
601
#[cfg(feature = "auto_register")]
602
pub fn no_auto_register(&self) -> bool {
603
self.no_auto_register
604
}
605
606
/// Returns true if the `opaque` attribute was found on this type.
607
pub fn is_opaque(&self) -> bool {
608
self.is_opaque
609
}
610
}
611
612
/// Adds an identifier to a vector of identifiers if it is not already present.
613
///
614
/// Returns an error if the identifier already exists in the list.
615
fn add_unique_ident(idents: &mut Vec<Ident>, ident: Ident) -> Result<(), syn::Error> {
616
let ident_name = ident.to_string();
617
if idents.iter().any(|i| i == ident_name.as_str()) {
618
return Err(syn::Error::new(ident.span(), CONFLICTING_TYPE_DATA_MESSAGE));
619
}
620
621
idents.push(ident);
622
Ok(())
623
}
624
625
/// Extract a boolean value from an expression.
626
///
627
/// The mapper exists so that the caller can conditionally choose to use the given
628
/// value or supply their own.
629
fn extract_bool(
630
value: &Expr,
631
mut mapper: impl FnMut(&LitBool) -> LitBool,
632
) -> Result<LitBool, syn::Error> {
633
match value {
634
Expr::Lit(syn::ExprLit {
635
lit: syn::Lit::Bool(lit),
636
..
637
}) => Ok(mapper(lit)),
638
_ => Err(syn::Error::new(value.span(), "Expected a boolean value")),
639
}
640
}
641
642