Skip to main content

haz_query/engine/
spec.rs

1//! Inputs and errors of the query engine.
2//!
3//! [`QuerySpec`] is the typed bundle of filters the CLI hands
4//! to the engine. Every field is optional; an absent filter
5//! does not constrain the candidate set per `QRY-006`.
6//! Atom-level validation has already happened at the lift
7//! layer in [`crate::expr`]; the engine consumes a well-typed
8//! spec.
9//!
10//! [`QueryError`] enumerates the failure modes the engine
11//! surfaces. Validation-class errors (unknown bearing project)
12//! and runtime-class errors (glob-intersection DFA build
13//! failure) are kept distinct so the CLI can render appropriate
14//! diagnostics.
15
16use haz_domain::name::{ProjectName, TagName, TaskName};
17use haz_domain::path::PathPattern;
18use haz_query_lang::expr::Expr;
19use snafu::Snafu;
20
21use crate::expr::glob_intersect::GlobIntersectError;
22use crate::expr::relational::RelationalAtom;
23use crate::expr::shortcut::BooleanShortcut;
24
25/// The full set of filters accepted by `haz query` per
26/// `QRY-001`. Each field corresponds to one CLI flag (or, for
27/// `shortcuts`, the collection of boolean-shortcut flags).
28///
29/// An absent (`None`) per-attribute or relational filter
30/// contributes no constraint per `QRY-006`. The `shortcuts`
31/// vector accumulates every boolean shortcut the user supplied;
32/// the engine intersects them against the candidate set.
33#[derive(Debug, Default, Clone)]
34pub struct QuerySpec {
35    /// `--tags <EXPR>` per `QRY-003`.
36    pub tags: Option<Expr<TagName>>,
37    /// `--projects <EXPR>` per `QRY-003`.
38    pub projects: Option<Expr<ProjectName>>,
39    /// `--tasks <EXPR>` per `QRY-003`.
40    pub tasks: Option<Expr<TaskName>>,
41    /// `--inputs <EXPR>` per `QRY-003`.
42    pub inputs: Option<Expr<PathPattern>>,
43    /// `--outputs <EXPR>` per `QRY-003`.
44    pub outputs: Option<Expr<PathPattern>>,
45    /// `--child-of <EXPR>` per `QRY-004`.
46    pub child_of: Option<Expr<RelationalAtom>>,
47    /// `--parent-of <EXPR>` per `QRY-004`.
48    pub parent_of: Option<Expr<RelationalAtom>>,
49    /// `--depends-on <EXPR>` per `QRY-004`.
50    pub depends_on: Option<Expr<RelationalAtom>>,
51    /// `--ancestor-of <EXPR>` per `QRY-004`.
52    pub ancestor_of: Option<Expr<RelationalAtom>>,
53    /// Boolean shortcuts per `QRY-005`. Multiple shortcuts
54    /// compose by intersection; the CLI parser enforces
55    /// within-pair mutual exclusion (`--has-X` vs `--no-X`).
56    pub shortcuts: Vec<BooleanShortcut>,
57}
58
59/// Failure produced by the query engine.
60///
61/// Maps to the `EXEC-021` internal / configuration-error class
62/// per `QRY-009`. Each variant carries enough context to render
63/// a precise CLI diagnostic.
64#[derive(Debug, Snafu)]
65pub enum QueryError {
66    /// The bearing project supplied by the caller does not
67    /// exist in the workspace. Should not occur when the
68    /// bearing project is derived from a cwd lookup via
69    /// `EXEC-022`, but the engine surfaces it as a typed error
70    /// rather than panicking.
71    #[snafu(display("bearing project '{name}' is not in the workspace"))]
72    BearingProjectNotInWorkspace {
73        /// The name of the missing project.
74        name: String,
75    },
76
77    /// A canonicalised path pattern failed to re-parse after
78    /// anchoring to its workspace-absolute form. Should not
79    /// occur for patterns that already passed validation; the
80    /// variant exists so the engine has a typed surface for
81    /// any future canonicalisation regressions.
82    #[snafu(display("could not re-parse canonicalised path pattern '{canonical}'"))]
83    CanonicalisePattern {
84        /// The offending canonicalised pattern string.
85        canonical: String,
86    },
87
88    /// The glob-glob intersection routine failed for one of the
89    /// `--inputs` / `--outputs` atoms.
90    #[snafu(display("glob intersection failed: {source}"))]
91    GlobIntersect {
92        /// The underlying intersection-routine failure.
93        source: GlobIntersectError,
94    },
95}