haz_query/expr/
shortcut.rs1use haz_domain::task::Task;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub enum BooleanShortcut {
22 HasDeps,
24 NoDeps,
26 HasInputs,
28 NoInputs,
30 HasOutputs,
32 NoOutputs,
34 Mutex,
37}
38
39impl BooleanShortcut {
40 #[must_use]
45 pub fn matches(&self, task: &Task) -> bool {
46 match self {
47 Self::HasDeps => !task.deps.is_empty(),
48 Self::NoDeps => task.deps.is_empty(),
49 Self::HasInputs => !task.inputs.is_empty(),
50 Self::NoInputs => task.inputs.is_empty(),
51 Self::HasOutputs => !task.outputs.is_empty(),
52 Self::NoOutputs => task.outputs.is_empty(),
53 Self::Mutex => task.mutex.is_some(),
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use std::str::FromStr;
61
62 use haz_domain::action::TaskAction;
63 use haz_domain::env::EnvSettings;
64 use haz_domain::mutex::{Mutex, MutexMode, MutexScope};
65 use haz_domain::name::{MutexName, TaskName};
66 use haz_domain::path::{InputSpec, OutputSpec};
67 use haz_domain::task::Task;
68 use haz_domain::task_ref::TaskRef;
69 use nonempty::NonEmpty;
70
71 use super::*;
72
73 fn argv(parts: &[&str]) -> NonEmpty<String> {
74 NonEmpty::from_vec(parts.iter().map(|s| (*s).to_owned()).collect()).unwrap()
75 }
76
77 fn bare_task(name: &str) -> Task {
78 Task {
79 name: TaskName::from_str(name).unwrap(),
80 action: TaskAction::Command(argv(&["true"])),
81 inputs: vec![],
82 outputs: vec![],
83 deps: vec![],
84 weak_deps: vec![],
85 mutex: None,
86 env: EnvSettings::default(),
87 }
88 }
89
90 #[test]
91 fn shortcut_variants_are_distinct() {
92 assert_ne!(BooleanShortcut::HasDeps, BooleanShortcut::NoDeps);
93 assert_ne!(BooleanShortcut::HasInputs, BooleanShortcut::NoInputs);
94 assert_ne!(BooleanShortcut::HasOutputs, BooleanShortcut::NoOutputs);
95 assert_ne!(BooleanShortcut::HasDeps, BooleanShortcut::HasInputs);
96 assert_ne!(BooleanShortcut::Mutex, BooleanShortcut::HasDeps);
97 }
98
99 #[test]
100 fn shortcut_variants_are_copy_and_hashable() {
101 use std::collections::HashSet;
102 let a = BooleanShortcut::HasDeps;
103 let b = a;
104 let mut set: HashSet<BooleanShortcut> = HashSet::new();
105 set.insert(a);
106 set.insert(b);
107 assert_eq!(set.len(), 1);
108 }
109
110 #[test]
113 fn qry_005_has_deps_and_no_deps_are_mutually_negating() {
114 let mut task = bare_task("t");
115 assert!(!BooleanShortcut::HasDeps.matches(&task));
117 assert!(BooleanShortcut::NoDeps.matches(&task));
118
119 task.deps.push(TaskRef::parse("~:other").unwrap());
120 assert!(BooleanShortcut::HasDeps.matches(&task));
121 assert!(!BooleanShortcut::NoDeps.matches(&task));
122 }
123
124 #[test]
125 fn qry_005_has_inputs_and_no_inputs_check_inputs_field() {
126 let mut task = bare_task("t");
127 assert!(BooleanShortcut::NoInputs.matches(&task));
128 assert!(!BooleanShortcut::HasInputs.matches(&task));
129
130 task.inputs.push(InputSpec::parse("src/main.rs").unwrap());
131 assert!(BooleanShortcut::HasInputs.matches(&task));
132 assert!(!BooleanShortcut::NoInputs.matches(&task));
133 }
134
135 #[test]
136 fn qry_005_has_outputs_and_no_outputs_check_outputs_field() {
137 let mut task = bare_task("t");
138 assert!(BooleanShortcut::NoOutputs.matches(&task));
139 assert!(!BooleanShortcut::HasOutputs.matches(&task));
140
141 task.outputs
142 .push(OutputSpec::parse("dist/bundle.js").unwrap());
143 assert!(BooleanShortcut::HasOutputs.matches(&task));
144 assert!(!BooleanShortcut::NoOutputs.matches(&task));
145 }
146
147 #[test]
148 fn qry_005_mutex_checks_mutex_field_presence() {
149 let mut task = bare_task("t");
150 assert!(!BooleanShortcut::Mutex.matches(&task));
151
152 task.mutex = Some(Mutex {
153 scope: MutexScope::Workspace,
154 name: MutexName::from_str("db").unwrap(),
155 mode: MutexMode::Exclusive,
156 });
157 assert!(BooleanShortcut::Mutex.matches(&task));
158 }
159}