use haz_dag::producer::anchor_to_workspace_absolute;
use haz_domain::path::{InputSpec, OutputSpec, PathPattern, ProjectRoot};
use haz_query_lang::expr::Expr;
use crate::engine::candidate::CandidateTask;
use crate::engine::spec::{QueryError, QuerySpec};
use crate::expr::path;
pub fn passes_non_relational(
candidate: &CandidateTask<'_>,
spec: &QuerySpec,
bearing_project_root: Option<&ProjectRoot>,
) -> Result<bool, QueryError> {
if let Some(expr) = &spec.tags
&& !expr.eval(|tag| candidate.project.tags.contains(tag))
{
return Ok(false);
}
if let Some(expr) = &spec.projects
&& !expr.eval(|name| candidate.project_name == name)
{
return Ok(false);
}
if let Some(expr) = &spec.tasks
&& !expr.eval(|name| candidate.task_name == name)
{
return Ok(false);
}
if let Some(expr) = &spec.inputs {
let task_canonical = canonicalise_task_patterns(
candidate.task.inputs.iter().map(InputSpec::pattern),
&candidate.project.root,
)?;
let matches = pattern_expr_matches(expr, &task_canonical, bearing_project_root)?;
if !matches {
return Ok(false);
}
}
if let Some(expr) = &spec.outputs {
let task_canonical = canonicalise_task_patterns(
candidate.task.outputs.iter().map(OutputSpec::pattern),
&candidate.project.root,
)?;
let matches = pattern_expr_matches(expr, &task_canonical, bearing_project_root)?;
if !matches {
return Ok(false);
}
}
for shortcut in &spec.shortcuts {
if !shortcut.matches(candidate.task) {
return Ok(false);
}
}
Ok(true)
}
fn canonicalise_task_patterns<'p>(
patterns: impl Iterator<Item = &'p PathPattern>,
project_root: &ProjectRoot,
) -> Result<Vec<PathPattern>, QueryError> {
patterns
.map(|pattern| canonicalise(pattern, project_root))
.collect()
}
fn pattern_expr_matches(
expr: &Expr<PathPattern>,
task_canonical: &[PathPattern],
bearing_project_root: Option<&ProjectRoot>,
) -> Result<bool, QueryError> {
expr.try_eval(|atom_pattern| {
let atom_canonical = canonicalise_user_atom(atom_pattern, bearing_project_root)?;
for task_pattern in task_canonical {
let hit = path::intersects(&atom_canonical, task_pattern)
.map_err(|source| QueryError::GlobIntersect { source })?;
if hit {
return Ok(true);
}
}
Ok(false)
})
}
fn canonicalise_user_atom(
atom: &PathPattern,
bearing_project_root: Option<&ProjectRoot>,
) -> Result<PathPattern, QueryError> {
if matches!(
atom.anchor(),
haz_domain::path::PathAnchor::WorkspaceAbsolute
) {
return Ok(atom.clone());
}
let Some(root) = bearing_project_root else {
return Err(QueryError::CanonicalisePattern {
canonical: atom.to_string(),
});
};
canonicalise(atom, root)
}
fn canonicalise(
pattern: &PathPattern,
project_root: &ProjectRoot,
) -> Result<PathPattern, QueryError> {
let canonical_string = anchor_to_workspace_absolute(pattern, project_root);
PathPattern::parse(&canonical_string).map_err(|_| QueryError::CanonicalisePattern {
canonical: canonical_string,
})
}