1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
34use crate::platform::{Platform, PlatformSpec};
5use std::ops::{BitAnd, BitOr};
6use target_spec::TargetSpec;
78/// The status of a dependency or feature, which is possibly platform-dependent.
9///
10/// This is a sub-status of [`EnabledStatus`](crate::graph::EnabledStatus).
11#[derive(Copy, Clone, Debug)]
12pub enum PlatformStatus<'g> {
13/// This dependency or feature is never enabled on any platforms.
14Never,
15/// This dependency or feature is always enabled on all platforms.
16Always,
17/// The status is platform-dependent.
18PlatformDependent {
19/// An evaluator to run queries against.
20eval: PlatformEval<'g>,
21 },
22}
2324assert_covariant!(PlatformStatus);
2526impl<'g> PlatformStatus<'g> {
27pub(crate) fn new(specs: &'g PlatformStatusImpl) -> Self {
28match specs {
29 PlatformStatusImpl::Always => PlatformStatus::Always,
30 PlatformStatusImpl::Specs(specs) => {
31if specs.is_empty() {
32 PlatformStatus::Never
33 } else {
34 PlatformStatus::PlatformDependent {
35 eval: PlatformEval { specs },
36 }
37 }
38 }
39 }
40 }
4142/// Returns true if this dependency is always enabled on all platforms.
43pub fn is_always(&self) -> bool {
44match self {
45 PlatformStatus::Always => true,
46 PlatformStatus::PlatformDependent { .. } | PlatformStatus::Never => false,
47 }
48 }
4950/// Returns true if this dependency is never enabled on any platform.
51pub fn is_never(&self) -> bool {
52match self {
53 PlatformStatus::Never => true,
54 PlatformStatus::PlatformDependent { .. } | PlatformStatus::Always => false,
55 }
56 }
5758/// Returns true if this dependency is possibly enabled on any platform.
59pub fn is_present(&self) -> bool {
60 !self.is_never()
61 }
6263/// Evaluates whether this dependency is enabled on the given platform spec.
64 ///
65 /// Returns `Unknown` if the result was unknown, which may happen if evaluating against an
66 /// individual platform and its target features are unknown.
67pub fn enabled_on(&self, platform_spec: &PlatformSpec) -> EnabledTernary {
68match (self, platform_spec) {
69 (PlatformStatus::Always, _) => EnabledTernary::Enabled,
70 (PlatformStatus::Never, _) => EnabledTernary::Disabled,
71 (PlatformStatus::PlatformDependent { .. }, PlatformSpec::Any) => {
72 EnabledTernary::Enabled
73 }
74 (PlatformStatus::PlatformDependent { eval }, PlatformSpec::Platform(platform)) => {
75 eval.eval(platform)
76 }
77 (PlatformStatus::PlatformDependent { .. }, PlatformSpec::Always) => {
78 EnabledTernary::Disabled
79 }
80 }
81 }
82}
8384/// Whether a dependency or feature is enabled on a specific platform.
85///
86/// This is a ternary or [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic)
87/// because the result may be unknown in some situations.
88///
89/// Returned by the methods on `EnabledStatus`, `PlatformStatus`, and `PlatformEval`.
90#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
91pub enum EnabledTernary {
92/// The dependency is disabled on this platform.
93Disabled,
94/// The status of this dependency is unknown on this platform.
95 ///
96 /// This may happen if evaluation involves unknown target features. Notably,
97 /// this will not be returned for [`Platform::build_target()`], since the
98 /// target features for the build target platform are determined at compile
99 /// time.
100Unknown,
101/// The dependency is enabled on this platform.
102Enabled,
103}
104105impl EnabledTernary {
106fn new(x: Option<bool>) -> Self {
107match x {
108Some(false) => EnabledTernary::Disabled,
109None => EnabledTernary::Unknown,
110Some(true) => EnabledTernary::Enabled,
111 }
112 }
113114/// Returns true if the status is known (either enabled or disabled).
115pub fn is_known(self) -> bool {
116match self {
117 EnabledTernary::Disabled | EnabledTernary::Enabled => true,
118 EnabledTernary::Unknown => false,
119 }
120 }
121}
122123/// AND operation in Kleene K3 logic.
124impl BitAnd for EnabledTernary {
125type Output = Self;
126127fn bitand(self, rhs: Self) -> Self::Output {
128use EnabledTernary::*;
129130match (self, rhs) {
131 (Enabled, Enabled) => Enabled,
132 (Disabled, _) | (_, Disabled) => Disabled,
133_ => Unknown,
134 }
135 }
136}
137138/// OR operation in Kleene K3 logic.
139impl BitOr for EnabledTernary {
140type Output = Self;
141142fn bitor(self, rhs: Self) -> Self {
143use EnabledTernary::*;
144145match (self, rhs) {
146 (Disabled, Disabled) => Disabled,
147 (Enabled, _) | (_, Enabled) => Enabled,
148_ => Unknown,
149 }
150 }
151}
152153/// An evaluator for platform-specific dependencies.
154///
155/// This represents a collection of platform specifications, of the sort `cfg(unix)`.
156#[derive(Copy, Clone, Debug)]
157pub struct PlatformEval<'g> {
158 specs: &'g [TargetSpec],
159}
160161assert_covariant!(PlatformEval);
162163impl<'g> PlatformEval<'g> {
164/// Runs this evaluator against the given platform.
165pub fn eval(&self, platform: &Platform) -> EnabledTernary {
166let mut res = EnabledTernary::Disabled;
167for spec in self.specs.iter() {
168let matches = spec.eval(platform);
169// Short-circuit evaluation if possible.
170if matches == Some(true) {
171return EnabledTernary::Enabled;
172 }
173 res = res | EnabledTernary::new(matches);
174 }
175 res
176 }
177178/// Returns the [`TargetSpec`] instances backing this evaluator.
179 ///
180 /// The result of [`PlatformEval::eval`] against a platform is a logical OR
181 /// of the results of evaluating the platform against each target spec.
182pub fn target_specs(&self) -> &'g [TargetSpec] {
183self.specs
184 }
185}
186187#[derive(Clone, Debug)]
188pub(crate) enum PlatformStatusImpl {
189 Always,
190// Empty vector means never.
191Specs(Vec<TargetSpec>),
192}
193194impl PlatformStatusImpl {
195/// Returns true if this is an empty predicate (i.e. will never match).
196pub(crate) fn is_never(&self) -> bool {
197match self {
198 PlatformStatusImpl::Always => false,
199 PlatformStatusImpl::Specs(specs) => specs.is_empty(),
200 }
201 }
202203pub(crate) fn extend(&mut self, other: &PlatformStatusImpl) {
204// &mut *self is a reborrow to allow *self to work below.
205match (&mut *self, other) {
206 (PlatformStatusImpl::Always, _) => {
207// Always stays the same since it means all specs are included.
208}
209 (PlatformStatusImpl::Specs(_), PlatformStatusImpl::Always) => {
210// Mark self as Always.
211*self = PlatformStatusImpl::Always;
212 }
213 (PlatformStatusImpl::Specs(specs), PlatformStatusImpl::Specs(other)) => {
214 specs.extend_from_slice(other.as_slice());
215 }
216 }
217 }
218219pub(crate) fn add_spec(&mut self, spec: Option<&TargetSpec>) {
220// &mut *self is a reborrow to allow *self to work below.
221match (&mut *self, spec) {
222 (PlatformStatusImpl::Always, _) => {
223// Always stays the same since it means all specs are included.
224}
225 (PlatformStatusImpl::Specs(_), None) => {
226// Mark self as Always.
227*self = PlatformStatusImpl::Always;
228 }
229 (PlatformStatusImpl::Specs(specs), Some(spec)) => {
230 specs.push(spec.clone());
231 }
232 }
233 }
234}
235236impl Default for PlatformStatusImpl {
237fn default() -> Self {
238// Empty vector means never.
239PlatformStatusImpl::Specs(vec![])
240 }
241}