turbopack_core/
condition.rs

1use anyhow::Result;
2use futures::{StreamExt, stream};
3use serde::{Deserialize, Serialize};
4use turbo_tasks::{NonLocalValue, ResolvedVc, trace::TraceRawVcs};
5use turbo_tasks_fs::FileSystemPath;
6
7#[derive(Debug, Clone, Serialize, Deserialize, TraceRawVcs, PartialEq, Eq, NonLocalValue)]
8pub enum ContextCondition {
9    All(Vec<ContextCondition>),
10    Any(Vec<ContextCondition>),
11    Not(Box<ContextCondition>),
12    InDirectory(String),
13    InPath(ResolvedVc<FileSystemPath>),
14}
15
16impl ContextCondition {
17    /// Creates a condition that matches if all of the given conditions match.
18    pub fn all(conditions: Vec<ContextCondition>) -> ContextCondition {
19        ContextCondition::All(conditions)
20    }
21
22    /// Creates a condition that matches if any of the given conditions match.
23    pub fn any(conditions: Vec<ContextCondition>) -> ContextCondition {
24        ContextCondition::Any(conditions)
25    }
26
27    /// Creates a condition that matches if the given condition does not match.
28    #[allow(clippy::should_implement_trait)]
29    pub fn not(condition: ContextCondition) -> ContextCondition {
30        ContextCondition::Not(Box::new(condition))
31    }
32
33    /// Returns true if the condition matches the context.
34    pub async fn matches(&self, path: &FileSystemPath) -> Result<bool> {
35        match self {
36            ContextCondition::All(conditions) => {
37                // False positive.
38                #[allow(clippy::manual_try_fold)]
39                stream::iter(conditions)
40                    .fold(Ok(true), |acc, c| async move {
41                        Ok(acc? && Box::pin(c.matches(path)).await?)
42                    })
43                    .await
44            }
45            ContextCondition::Any(conditions) => {
46                // False positive.
47                #[allow(clippy::manual_try_fold)]
48                stream::iter(conditions)
49                    .fold(Ok(false), |acc, c| async move {
50                        Ok(acc? || Box::pin(c.matches(path)).await?)
51                    })
52                    .await
53            }
54            ContextCondition::Not(condition) => Box::pin(condition.matches(path)).await.map(|b| !b),
55            ContextCondition::InPath(other_path) => {
56                Ok(path.is_inside_or_equal_ref(&*other_path.await?))
57            }
58            ContextCondition::InDirectory(dir) => Ok(path.path.starts_with(&format!("{dir}/"))
59                || path.path.contains(&format!("/{dir}/"))
60                || path.path.ends_with(&format!("/{dir}"))
61                || path.path == *dir),
62        }
63    }
64}