Skip to main content

projectdetect/
indicator.rs

1//! Indicators (match rules) and the [`Match`] result type.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// A single match rule evaluated against a directory's listing.
7///
8/// Indicators are OR'd within a [`crate::ProjectType`]: the first one that
9/// fires identifies the directory as that type. The variant determines what
10/// the rule sees:
11///
12/// - [`Indicator::HasFile`] — case-insensitive exact **file** basename match
13///   (ASCII fold, e.g. `NuGet.Config` matches `nuget.config`).
14/// - [`Indicator::HasGlob`] — glob over **file** basenames only
15///   (e.g. `*.tf`). Directories are never matched.
16/// - [`Indicator::HasSubdirGlob`] — glob over immediate **subdirectory**
17///   basenames only (e.g. `*.xcodeproj`). Files are never matched.
18/// - [`Indicator::Cel`] — a CEL expression over the `files` and `subdirs`
19///   lists. Requires the `cel` cargo feature; without it, registering a type
20///   with this indicator fails with [`crate::Error::CelFeatureDisabled`].
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum Indicator {
23    /// Case-insensitive exact file-basename match.
24    HasFile(String),
25    /// Glob over file basenames.
26    HasGlob(String),
27    /// Glob over immediate-subdirectory basenames.
28    HasSubdirGlob(String),
29    /// CEL expression over `files` / `subdirs` (requires the `cel` feature).
30    Cel(String),
31}
32
33impl fmt::Display for Indicator {
34    /// Human-readable form surfaced in [`Match::indicator`] for "why did this
35    /// match" debuggability. A `HasSubdirGlob` renders with a trailing slash
36    /// (e.g. `MyApp.xcodeproj/`) to signal a directory marker; a `Cel`
37    /// indicator renders as `cel:<expr>`.
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        match self {
40            Indicator::HasFile(s) | Indicator::HasGlob(s) => f.write_str(s),
41            Indicator::HasSubdirGlob(s) => write!(f, "{s}/"),
42            Indicator::Cel(e) => write!(f, "cel:{e}"),
43        }
44    }
45}
46
47/// Couples a matched project type with the indicator that fired. Surfaced by
48/// [`crate::Registry::detect`] so consumers can audit detection decisions.
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
50pub struct Match {
51    /// The [`crate::ProjectType::name`] that matched.
52    #[serde(rename = "type")]
53    pub r#type: String,
54    /// The [`Indicator`]'s [`Display`](std::fmt::Display) form that fired.
55    pub indicator: String,
56}