cfg_expr/
expr.rs

1pub mod lexer;
2mod parser;
3
4use smallvec::SmallVec;
5use std::ops::Range;
6
7/// A predicate function, used to combine 1 or more predicates
8/// into a single value
9#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
10pub enum Func {
11    /// `not()` with a configuration predicate. It is true if its predicate
12    /// is false and false if its predicate is true.
13    Not,
14    /// `all()` with a comma separated list of configuration predicates. It
15    /// is false if at least one predicate is false. If there are no predicates,
16    /// it is true.
17    ///
18    /// The associated `usize` is the number of predicates inside the `all()`.
19    All(usize),
20    /// `any()` with a comma separated list of configuration predicates. It
21    /// is true if at least one predicate is true. If there are no predicates,
22    /// it is false.
23    ///
24    /// The associated `usize` is the number of predicates inside the `any()`.
25    Any(usize),
26}
27
28use crate::targets as targ;
29
30/// All predicates that pertains to a target, except for `target_feature`
31#[derive(Clone, PartialEq, Eq, Debug)]
32pub enum TargetPredicate {
33    /// [target_abi](https://github.com/rust-lang/rust/issues/80970)
34    Abi(targ::Abi),
35    /// [target_arch](https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch)
36    Arch(targ::Arch),
37    /// [target_endian](https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian)
38    Endian(targ::Endian),
39    /// [target_env](https://doc.rust-lang.org/reference/conditional-compilation.html#target_env)
40    Env(targ::Env),
41    /// [target_family](https://doc.rust-lang.org/reference/conditional-compilation.html#target_family)
42    /// This also applies to the bare [`unix` and `windows`](https://doc.rust-lang.org/reference/conditional-compilation.html#unix-and-windows)
43    /// predicates.
44    Family(targ::Family),
45    /// [target_has_atomic](https://doc.rust-lang.org/reference/conditional-compilation.html#target_has_atomic).
46    HasAtomic(targ::HasAtomic),
47    /// [target_os](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os)
48    Os(targ::Os),
49    /// [panic](https://doc.rust-lang.org/reference/conditional-compilation.html#panic)
50    Panic(targ::Panic),
51    /// [target_pointer_width](https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width)
52    PointerWidth(u8),
53    /// [target_vendor](https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor)
54    Vendor(targ::Vendor),
55}
56
57pub trait TargetMatcher {
58    fn matches(&self, tp: &TargetPredicate) -> bool;
59}
60
61impl TargetMatcher for targ::TargetInfo {
62    fn matches(&self, tp: &TargetPredicate) -> bool {
63        use TargetPredicate::{
64            Abi, Arch, Endian, Env, Family, HasAtomic, Os, Panic, PointerWidth, Vendor,
65        };
66
67        match tp {
68            // The ABI is allowed to be an empty string
69            Abi(abi) => match &self.abi {
70                Some(a) => abi == a,
71                None => abi.0.is_empty(),
72            },
73            Arch(a) => a == &self.arch,
74            Endian(end) => *end == self.endian,
75            // The environment is allowed to be an empty string
76            Env(env) => match &self.env {
77                Some(e) => env == e,
78                None => env.0.is_empty(),
79            },
80            Family(fam) => self.families.contains(fam),
81            HasAtomic(has_atomic) => self.has_atomics.contains(*has_atomic),
82            Os(os) => match &self.os {
83                Some(self_os) => os == self_os,
84                // os = "none" means it should be matched against None. Note that this is different
85                // from "env" above.
86                None => os.as_str() == "none",
87            },
88            PointerWidth(w) => *w == self.pointer_width,
89            Vendor(ven) => match &self.vendor {
90                Some(v) => ven == v,
91                None => ven == &targ::Vendor::unknown,
92            },
93            Panic(panic) => &self.panic == panic,
94        }
95    }
96}
97
98#[cfg(feature = "targets")]
99impl TargetMatcher for target_lexicon::Triple {
100    #[allow(clippy::cognitive_complexity)]
101    #[allow(clippy::match_same_arms)]
102    fn matches(&self, tp: &TargetPredicate) -> bool {
103        use target_lexicon::*;
104        use TargetPredicate::{
105            Abi, Arch, Endian, Env, Family, HasAtomic, Os, Panic, PointerWidth, Vendor,
106        };
107
108        match tp {
109            Abi(_) => {
110                // `target_abi` is unstable. Assume false for this.
111                false
112            }
113            Arch(arch) => {
114                if arch == &targ::Arch::x86 {
115                    matches!(self.architecture, Architecture::X86_32(_))
116                } else if arch == &targ::Arch::wasm32 {
117                    self.architecture == Architecture::Wasm32
118                        || self.architecture == Architecture::Asmjs
119                } else if arch == &targ::Arch::arm {
120                    matches!(self.architecture, Architecture::Arm(_))
121                } else if arch == &targ::Arch::bpf {
122                    self.architecture == Architecture::Bpfeb
123                        || self.architecture == Architecture::Bpfel
124                } else if arch == &targ::Arch::x86_64 {
125                    self.architecture == Architecture::X86_64
126                        || self.architecture == Architecture::X86_64h
127                } else if arch == &targ::Arch::mips32r6 {
128                    matches!(
129                        self.architecture,
130                        Architecture::Mips32(
131                            Mips32Architecture::Mipsisa32r6 | Mips32Architecture::Mipsisa32r6el
132                        )
133                    )
134                } else if arch == &targ::Arch::mips64r6 {
135                    matches!(
136                        self.architecture,
137                        Architecture::Mips64(
138                            Mips64Architecture::Mipsisa64r6 | Mips64Architecture::Mipsisa64r6el
139                        )
140                    )
141                } else {
142                    match arch.0.parse::<Architecture>() {
143                        Ok(a) => match (self.architecture, a) {
144                            (Architecture::Aarch64(_), Architecture::Aarch64(_))
145                            | (Architecture::Mips32(_), Architecture::Mips32(_))
146                            | (Architecture::Mips64(_), Architecture::Mips64(_))
147                            | (Architecture::Powerpc64le, Architecture::Powerpc64)
148                            | (Architecture::Riscv32(_), Architecture::Riscv32(_))
149                            | (Architecture::Riscv64(_), Architecture::Riscv64(_))
150                            | (Architecture::Sparcv9, Architecture::Sparc64) => true,
151                            (a, b) => a == b,
152                        },
153                        Err(_) => false,
154                    }
155                }
156            }
157            Endian(end) => match self.architecture.endianness() {
158                Ok(endian) => matches!(
159                    (end, endian),
160                    (crate::targets::Endian::little, Endianness::Little)
161                        | (crate::targets::Endian::big, Endianness::Big)
162                ),
163
164                Err(_) => false,
165            },
166            Env(env) => {
167                // The environment is implied by some operating systems
168                match self.operating_system {
169                    OperatingSystem::Redox => env == &targ::Env::relibc,
170                    OperatingSystem::VxWorks => env == &targ::Env::gnu,
171                    OperatingSystem::Freebsd => match self.architecture {
172                        Architecture::Arm(ArmArchitecture::Armv6 | ArmArchitecture::Armv7) => {
173                            env == &targ::Env::gnueabihf
174                        }
175                        _ => env.0.is_empty(),
176                    },
177                    OperatingSystem::Netbsd => match self.architecture {
178                        Architecture::Arm(ArmArchitecture::Armv6 | ArmArchitecture::Armv7) => {
179                            env == &targ::Env::eabihf
180                        }
181                        _ => env.0.is_empty(),
182                    },
183                    OperatingSystem::None_
184                    | OperatingSystem::Cloudabi
185                    | OperatingSystem::Hermit
186                    | OperatingSystem::Ios => match self.environment {
187                        Environment::LinuxKernel => env == &targ::Env::gnu,
188                        _ => env.0.is_empty(),
189                    },
190                    _ => {
191                        if env.0.is_empty() {
192                            matches!(
193                                self.environment,
194                                Environment::Unknown
195                                    | Environment::Android
196                                    | Environment::Softfloat
197                                    | Environment::Androideabi
198                                    | Environment::Eabi
199                                    | Environment::Eabihf
200                                    | Environment::Sim
201                            )
202                        } else {
203                            match env.0.parse::<Environment>() {
204                                Ok(e) => {
205                                    // Rustc shortens multiple "gnu*" environments to just "gnu"
206                                    if env == &targ::Env::gnu {
207                                        match self.environment {
208                                            Environment::Gnu
209                                            | Environment::Gnuabi64
210                                            | Environment::Gnueabi
211                                            | Environment::Gnuspe
212                                            | Environment::Gnux32
213                                            | Environment::GnuIlp32
214                                            | Environment::Gnueabihf
215                                            | Environment::GnuLlvm => true,
216                                            // Rust 1.49.0 changed all android targets to have the
217                                            // gnu environment
218                                            Environment::Android | Environment::Androideabi
219                                                if self.operating_system
220                                                    == OperatingSystem::Linux =>
221                                            {
222                                                true
223                                            }
224                                            Environment::Kernel => {
225                                                self.operating_system == OperatingSystem::Linux
226                                            }
227                                            _ => false,
228                                        }
229                                    } else if env == &targ::Env::musl {
230                                        matches!(
231                                            self.environment,
232                                            Environment::Musl
233                                                | Environment::Musleabi
234                                                | Environment::Musleabihf
235                                                | Environment::Muslabi64
236                                        )
237                                    } else if env == &targ::Env::uclibc {
238                                        matches!(
239                                            self.environment,
240                                            Environment::Uclibc
241                                                | Environment::Uclibceabi
242                                                | Environment::Uclibceabihf
243                                        )
244                                    } else if env == &targ::Env::newlib {
245                                        matches!(
246                                            self.operating_system,
247                                            OperatingSystem::Horizon | OperatingSystem::Espidf
248                                        )
249                                    } else {
250                                        self.environment == e
251                                    }
252                                }
253                                Err(_) => false,
254                            }
255                        }
256                    }
257                }
258            }
259            Family(fam) => {
260                use OperatingSystem::{
261                    Aix, AmdHsa, Bitrig, Cloudabi, Cuda, Darwin, Dragonfly, Emscripten, Espidf,
262                    Freebsd, Fuchsia, Haiku, Hermit, Horizon, Illumos, Ios, L4re, Linux, MacOSX,
263                    Nebulet, Netbsd, None_, Openbsd, Redox, Solaris, Tvos, Uefi, Unknown, VxWorks,
264                    Wasi, Watchos, Windows,
265                };
266                match self.operating_system {
267                    AmdHsa | Bitrig | Cloudabi | Cuda | Hermit | Nebulet | None_ | Uefi => false,
268                    Aix
269                    | Darwin
270                    | Dragonfly
271                    | Espidf
272                    | Freebsd
273                    | Fuchsia
274                    | Haiku
275                    | Illumos
276                    | Ios
277                    | L4re
278                    | MacOSX { .. }
279                    | Horizon
280                    | Netbsd
281                    | Openbsd
282                    | Redox
283                    | Solaris
284                    | Tvos
285                    | VxWorks
286                    | Watchos => fam == &crate::targets::Family::unix,
287                    Emscripten => {
288                        match self.architecture {
289                            // asmjs, wasm32 and wasm64 are part of both the wasm and unix families
290                            Architecture::Asmjs | Architecture::Wasm32 => {
291                                fam == &crate::targets::Family::wasm
292                                    || fam == &crate::targets::Family::unix
293                            }
294                            _ => false,
295                        }
296                    }
297                    Unknown => {
298                        // asmjs, wasm32 and wasm64 are part of the wasm family.
299                        match self.architecture {
300                            Architecture::Asmjs | Architecture::Wasm32 | Architecture::Wasm64 => {
301                                fam == &crate::targets::Family::wasm
302                            }
303                            _ => false,
304                        }
305                    }
306                    Linux => {
307                        // The 'kernel' environment is treated specially as not-unix
308                        if self.environment != Environment::Kernel {
309                            fam == &crate::targets::Family::unix
310                        } else {
311                            false
312                        }
313                    }
314                    Wasi => fam == &crate::targets::Family::wasm,
315                    Windows => fam == &crate::targets::Family::windows,
316                    // I really dislike non-exhaustive :(
317                    _ => false,
318                }
319            }
320            HasAtomic(_) => {
321                // atomic support depends on both the architecture and the OS. Assume false for
322                // this.
323                false
324            }
325            Os(os) => match os.0.parse::<OperatingSystem>() {
326                Ok(o) => match self.environment {
327                    Environment::HermitKernel => os == &targ::Os::hermit,
328                    _ => self.operating_system == o,
329                },
330                Err(_) => {
331                    // Handle special case for darwin/macos, where the triple is
332                    // "darwin", but rustc identifies the OS as "macos"
333                    if os == &targ::Os::macos && self.operating_system == OperatingSystem::Darwin {
334                        true
335                    } else {
336                        // For android, the os is still linux, but the environment is android
337                        os == &targ::Os::android
338                            && self.operating_system == OperatingSystem::Linux
339                            && (self.environment == Environment::Android
340                                || self.environment == Environment::Androideabi)
341                    }
342                }
343            },
344            Panic(_) => {
345                // panic support depends on the OS. Assume false for this.
346                false
347            }
348            Vendor(ven) => match ven.0.parse::<target_lexicon::Vendor>() {
349                Ok(v) => {
350                    if self.vendor == v {
351                        true
352                    } else if let target_lexicon::Vendor::Custom(custom) = &self.vendor {
353                        custom.as_str() == "esp" && v == target_lexicon::Vendor::Espressif
354                    } else {
355                        false
356                    }
357                }
358                Err(_) => false,
359            },
360            PointerWidth(pw) => {
361                // The gnux32 environment is a special case, where it has an
362                // x86_64 architecture, but a 32-bit pointer width
363                if !matches!(
364                    self.environment,
365                    Environment::Gnux32 | Environment::GnuIlp32
366                ) {
367                    *pw == match self.pointer_width() {
368                        Ok(pw) => pw.bits(),
369                        Err(_) => return false,
370                    }
371                } else {
372                    *pw == 32
373                }
374            }
375        }
376    }
377}
378
379impl TargetPredicate {
380    /// Returns true of the predicate matches the specified target
381    ///
382    /// Note that when matching against a [`target_lexicon::Triple`], the
383    /// `has_target_atomic` and `panic` predicates will _always_ return `false`.
384    ///
385    /// ```
386    /// use cfg_expr::{targets::*, expr::TargetPredicate as tp};
387    /// let win = get_builtin_target_by_triple("x86_64-pc-windows-msvc").unwrap();
388    ///
389    /// assert!(
390    ///     tp::Arch(Arch::x86_64).matches(win) &&
391    ///     tp::Endian(Endian::little).matches(win) &&
392    ///     tp::Env(Env::msvc).matches(win) &&
393    ///     tp::Family(Family::windows).matches(win) &&
394    ///     tp::Os(Os::windows).matches(win) &&
395    ///     tp::PointerWidth(64).matches(win) &&
396    ///     tp::Vendor(Vendor::pc).matches(win)
397    /// );
398    /// ```
399    pub fn matches<T>(&self, target: &T) -> bool
400    where
401        T: TargetMatcher,
402    {
403        target.matches(self)
404    }
405}
406
407#[derive(Clone, Debug)]
408pub(crate) enum Which {
409    Abi,
410    Arch,
411    Endian(targ::Endian),
412    Env,
413    Family,
414    Os,
415    HasAtomic(targ::HasAtomic),
416    Panic,
417    PointerWidth(u8),
418    Vendor,
419}
420
421#[derive(Clone, Debug)]
422pub(crate) struct InnerTarget {
423    which: Which,
424    span: Option<Range<usize>>,
425}
426
427/// A single predicate in a `cfg()` expression
428#[derive(Debug, PartialEq, Eq)]
429pub enum Predicate<'a> {
430    /// A target predicate, with the `target_` prefix
431    Target(TargetPredicate),
432    /// Whether rustc's test harness is [enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#test)
433    Test,
434    /// [Enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions)
435    /// when compiling without optimizations.
436    DebugAssertions,
437    /// [Enabled](https://doc.rust-lang.org/reference/conditional-compilation.html#proc_macro) for
438    /// crates of the proc_macro type.
439    ProcMacro,
440    /// A [`feature = "<name>"`](https://doc.rust-lang.org/nightly/cargo/reference/features.html)
441    Feature(&'a str),
442    /// [target_feature](https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature)
443    TargetFeature(&'a str),
444    /// A generic bare predicate key that doesn't match one of the known options, eg `cfg(bare)`
445    Flag(&'a str),
446    /// A generic key = "value" predicate that doesn't match one of the known options, eg `cfg(foo = "bar")`
447    KeyValue { key: &'a str, val: &'a str },
448}
449
450#[derive(Clone, Debug)]
451pub(crate) enum InnerPredicate {
452    Target(InnerTarget),
453    Test,
454    DebugAssertions,
455    ProcMacro,
456    Feature(Range<usize>),
457    TargetFeature(Range<usize>),
458    Other {
459        identifier: Range<usize>,
460        value: Option<Range<usize>>,
461    },
462}
463
464impl InnerPredicate {
465    fn to_pred<'a>(&self, s: &'a str) -> Predicate<'a> {
466        use InnerPredicate as IP;
467        use Predicate::{
468            DebugAssertions, Feature, Flag, KeyValue, ProcMacro, Target, TargetFeature, Test,
469        };
470
471        match self {
472            IP::Target(it) => match &it.which {
473                Which::Abi => Target(TargetPredicate::Abi(targ::Abi::new(
474                    s[it.span.clone().unwrap()].to_owned(),
475                ))),
476                Which::Arch => Target(TargetPredicate::Arch(targ::Arch::new(
477                    s[it.span.clone().unwrap()].to_owned(),
478                ))),
479                Which::Os => Target(TargetPredicate::Os(targ::Os::new(
480                    s[it.span.clone().unwrap()].to_owned(),
481                ))),
482                Which::Vendor => Target(TargetPredicate::Vendor(targ::Vendor::new(
483                    s[it.span.clone().unwrap()].to_owned(),
484                ))),
485                Which::Env => Target(TargetPredicate::Env(targ::Env::new(
486                    s[it.span.clone().unwrap()].to_owned(),
487                ))),
488                Which::Family => Target(TargetPredicate::Family(targ::Family::new(
489                    s[it.span.clone().unwrap()].to_owned(),
490                ))),
491                Which::Endian(end) => Target(TargetPredicate::Endian(*end)),
492                Which::HasAtomic(has_atomic) => Target(TargetPredicate::HasAtomic(*has_atomic)),
493                Which::Panic => Target(TargetPredicate::Panic(targ::Panic::new(
494                    s[it.span.clone().unwrap()].to_owned(),
495                ))),
496                Which::PointerWidth(pw) => Target(TargetPredicate::PointerWidth(*pw)),
497            },
498            IP::Test => Test,
499            IP::DebugAssertions => DebugAssertions,
500            IP::ProcMacro => ProcMacro,
501            IP::Feature(rng) => Feature(&s[rng.clone()]),
502            IP::TargetFeature(rng) => TargetFeature(&s[rng.clone()]),
503            IP::Other { identifier, value } => match value {
504                Some(vs) => KeyValue {
505                    key: &s[identifier.clone()],
506                    val: &s[vs.clone()],
507                },
508                None => Flag(&s[identifier.clone()]),
509            },
510        }
511    }
512}
513
514#[derive(Clone, Debug)]
515pub(crate) enum ExprNode {
516    Fn(Func),
517    Predicate(InnerPredicate),
518}
519
520/// A parsed `cfg()` expression that can evaluated
521#[derive(Clone, Debug)]
522pub struct Expression {
523    pub(crate) expr: SmallVec<[ExprNode; 5]>,
524    // We keep the original string around for providing the arbitrary
525    // strings that can make up an expression
526    pub(crate) original: String,
527}
528
529impl Expression {
530    /// An iterator over each predicate in the expression
531    pub fn predicates(&self) -> impl Iterator<Item = Predicate<'_>> {
532        self.expr.iter().filter_map(move |item| match item {
533            ExprNode::Predicate(pred) => {
534                let pred = pred.clone().to_pred(&self.original);
535                Some(pred)
536            }
537            ExprNode::Fn(_) => None,
538        })
539    }
540
541    /// Evaluates the expression, using the provided closure to determine the value of
542    /// each predicate, which are then combined into a final result depending on the
543    /// functions `not()`, `all()`, or `any()` in the expression.
544    ///
545    /// `eval_predicate` typically returns `bool`, but may return any type that implements
546    /// the `Logic` trait.
547    ///
548    /// ## Examples
549    ///
550    /// ```
551    /// use cfg_expr::{targets::*, Expression, Predicate};
552    ///
553    /// let linux_musl = get_builtin_target_by_triple("x86_64-unknown-linux-musl").unwrap();
554    ///
555    /// let expr = Expression::parse(r#"all(not(windows), target_env = "musl", any(target_arch = "x86", target_arch = "x86_64"))"#).unwrap();
556    ///
557    /// assert!(expr.eval(|pred| {
558    ///     match pred {
559    ///         Predicate::Target(tp) => tp.matches(linux_musl),
560    ///         _ => false,
561    ///     }
562    /// }));
563    /// ```
564    ///
565    /// Returning `Option<bool>`, where `None` indicates the result is unknown:
566    ///
567    /// ```
568    /// use cfg_expr::{targets::*, Expression, Predicate};
569    ///
570    /// let expr = Expression::parse(r#"any(target_feature = "sse2", target_env = "musl")"#).unwrap();
571    ///
572    /// let linux_gnu = get_builtin_target_by_triple("x86_64-unknown-linux-gnu").unwrap();
573    /// let linux_musl = get_builtin_target_by_triple("x86_64-unknown-linux-musl").unwrap();
574    ///
575    /// fn eval(expr: &Expression, target: &TargetInfo) -> Option<bool> {
576    ///     expr.eval(|pred| {
577    ///         match pred {
578    ///             Predicate::Target(tp) => Some(tp.matches(target)),
579    ///             Predicate::TargetFeature(_) => None,
580    ///             _ => panic!("unexpected predicate"),
581    ///         }
582    ///     })
583    /// }
584    ///
585    /// // Whether the target feature is present is unknown, so the whole expression evaluates to
586    /// // None (unknown).
587    /// assert_eq!(eval(&expr, linux_gnu), None);
588    ///
589    /// // Whether the target feature is present is irrelevant for musl, since the any() always
590    /// // evaluates to true.
591    /// assert_eq!(eval(&expr, linux_musl), Some(true));
592    /// ```
593    pub fn eval<EP, T>(&self, mut eval_predicate: EP) -> T
594    where
595        EP: FnMut(&Predicate<'_>) -> T,
596        T: Logic + std::fmt::Debug,
597    {
598        let mut result_stack = SmallVec::<[T; 8]>::new();
599
600        // We store the expression as postfix, so just evaluate each license
601        // requirement in the order it comes, and then combining the previous
602        // results according to each operator as it comes
603        for node in self.expr.iter() {
604            match node {
605                ExprNode::Predicate(pred) => {
606                    let pred = pred.to_pred(&self.original);
607
608                    result_stack.push(eval_predicate(&pred));
609                }
610                ExprNode::Fn(Func::All(count)) => {
611                    // all() with a comma separated list of configuration predicates.
612                    let mut result = T::top();
613
614                    for _ in 0..*count {
615                        let r = result_stack.pop().unwrap();
616                        result = result.and(r);
617                    }
618
619                    result_stack.push(result);
620                }
621                ExprNode::Fn(Func::Any(count)) => {
622                    // any() with a comma separated list of configuration predicates.
623                    let mut result = T::bottom();
624
625                    for _ in 0..*count {
626                        let r = result_stack.pop().unwrap();
627                        result = result.or(r);
628                    }
629
630                    result_stack.push(result);
631                }
632                ExprNode::Fn(Func::Not) => {
633                    // not() with a configuration predicate.
634                    // It is true if its predicate is false
635                    // and false if its predicate is true.
636                    let r = result_stack.pop().unwrap();
637                    result_stack.push(r.not());
638                }
639            }
640        }
641
642        result_stack.pop().unwrap()
643    }
644
645    /// The original string which has been parsed to produce this [`Expression`].
646    ///
647    /// ```
648    /// use cfg_expr::Expression;
649    ///
650    /// assert_eq!(
651    ///     Expression::parse("any()").unwrap().original(),
652    ///     "any()"
653    /// );
654    /// ```
655    #[inline]
656    pub fn original(&self) -> &str {
657        &self.original
658    }
659}
660
661/// [`PartialEq`] will do a **syntactical** comparison, so will just check if both
662/// expressions have been parsed from the same string, **not** if they are semantically
663/// equivalent.
664///
665/// ```
666/// use cfg_expr::Expression;
667///
668/// assert_eq!(
669///     Expression::parse("any()").unwrap(),
670///     Expression::parse("any()").unwrap()
671/// );
672/// assert_ne!(
673///     Expression::parse("any()").unwrap(),
674///     Expression::parse("unix").unwrap()
675/// );
676/// ```
677impl PartialEq for Expression {
678    fn eq(&self, other: &Self) -> bool {
679        self.original.eq(&other.original)
680    }
681}
682
683/// A propositional logic used to evaluate `Expression` instances.
684///
685/// An `Expression` consists of some predicates and the `any`, `all` and `not` operators. An
686/// implementation of `Logic` defines how the `any`, `all` and `not` operators should be evaluated.
687pub trait Logic {
688    /// The result of an `all` operation with no operands, akin to Boolean `true`.
689    fn top() -> Self;
690
691    /// The result of an `any` operation with no operands, akin to Boolean `false`.
692    fn bottom() -> Self;
693
694    /// `AND`, which corresponds to the `all` operator.
695    fn and(self, other: Self) -> Self;
696
697    /// `OR`, which corresponds to the `any` operator.
698    fn or(self, other: Self) -> Self;
699
700    /// `NOT`, which corresponds to the `not` operator.
701    fn not(self) -> Self;
702}
703
704/// A boolean logic.
705impl Logic for bool {
706    #[inline]
707    fn top() -> Self {
708        true
709    }
710
711    #[inline]
712    fn bottom() -> Self {
713        false
714    }
715
716    #[inline]
717    fn and(self, other: Self) -> Self {
718        self && other
719    }
720
721    #[inline]
722    fn or(self, other: Self) -> Self {
723        self || other
724    }
725
726    #[inline]
727    fn not(self) -> Self {
728        !self
729    }
730}
731
732/// A three-valued logic -- `None` stands for the value being unknown.
733///
734/// The truth tables for this logic are described on
735/// [Wikipedia](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics).
736impl Logic for Option<bool> {
737    #[inline]
738    fn top() -> Self {
739        Some(true)
740    }
741
742    #[inline]
743    fn bottom() -> Self {
744        Some(false)
745    }
746
747    #[inline]
748    fn and(self, other: Self) -> Self {
749        match (self, other) {
750            // If either is false, the expression is false.
751            (Some(false), _) | (_, Some(false)) => Some(false),
752            // If both are true, the expression is true.
753            (Some(true), Some(true)) => Some(true),
754            // One or both are unknown -- the result is unknown.
755            _ => None,
756        }
757    }
758
759    #[inline]
760    fn or(self, other: Self) -> Self {
761        match (self, other) {
762            // If either is true, the expression is true.
763            (Some(true), _) | (_, Some(true)) => Some(true),
764            // If both are false, the expression is false.
765            (Some(false), Some(false)) => Some(false),
766            // One or both are unknown -- the result is unknown.
767            _ => None,
768        }
769    }
770
771    #[inline]
772    fn not(self) -> Self {
773        self.map(|v| !v)
774    }
775}