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}