1pub mod ast;
5pub mod builder;
8pub mod condition_op;
10
11macro_rules! define_file_size_condition {
12 ($module:ident, $type_name:ident, $op_id:literal, $predicate:ident) => {
13 pub mod $module {
15 use vyre_foundation::ir::{Expr, Program};
16
17 #[derive(Debug, Clone, Copy, Default)]
19 pub struct $type_name;
20
21 impl $type_name {
22 #[must_use]
24 pub fn program() -> Program {
25 crate::rule::condition_op::condition_program(OP_ID, || {
26 Expr::$predicate(
27 crate::rule::condition_op::file_size(),
28 crate::rule::condition_op::threshold(),
29 )
30 })
31 }
32 }
33
34 pub const OP_ID: &str = $op_id;
36
37 pub const CONTRACT: vyre_spec::OperationContract =
39 crate::contracts::RULE_PREDICATE_CHEAP;
40 }
41 };
42}
43
44define_file_size_condition!(
45 file_size_eq,
46 FileSizeEq,
47 "vyre-libs::rule::file_size_eq",
48 eq
49);
50define_file_size_condition!(
51 file_size_gt,
52 FileSizeGt,
53 "vyre-libs::rule::file_size_gt",
54 gt
55);
56define_file_size_condition!(
57 file_size_gte,
58 FileSizeGte,
59 "vyre-libs::rule::file_size_gte",
60 ge
61);
62define_file_size_condition!(
63 file_size_lt,
64 FileSizeLt,
65 "vyre-libs::rule::file_size_lt",
66 lt
67);
68define_file_size_condition!(
69 file_size_lte,
70 FileSizeLte,
71 "vyre-libs::rule::file_size_lte",
72 le
73);
74define_file_size_condition!(
75 file_size_ne,
76 FileSizeNe,
77 "vyre-libs::rule::file_size_ne",
78 ne
79);
80
81macro_rules! define_pattern_count_condition {
82 ($module:ident, $type_name:ident, $op_id:literal, $predicate:ident) => {
83 pub mod $module {
85 use vyre_foundation::ir::{Expr, Program};
86
87 #[derive(Debug, Clone, Copy, Default)]
89 pub struct $type_name;
90
91 impl $type_name {
92 #[must_use]
94 pub fn program() -> Program {
95 crate::rule::condition_op::condition_program(OP_ID, || {
96 Expr::$predicate(
97 crate::rule::condition_op::pattern_count(),
98 crate::rule::condition_op::threshold(),
99 )
100 })
101 }
102 }
103
104 pub const OP_ID: &str = $op_id;
106
107 pub const CONTRACT: vyre_spec::OperationContract =
109 crate::contracts::RULE_PREDICATE_CHEAP;
110 }
111 };
112}
113
114define_pattern_count_condition!(
115 pattern_count_gt,
116 PatternCountGt,
117 "vyre-libs::rule::pattern_count_gt",
118 gt
119);
120define_pattern_count_condition!(
121 pattern_count_gte,
122 PatternCountGte,
123 "vyre-libs::rule::pattern_count_gte",
124 ge
125);
126
127pub mod literal_false;
129pub mod literal_true;
131pub mod pattern_exists;
133pub mod reference_eval;
137
138pub use ast::{RuleCondition, RuleFormula};
139pub use builder::build_rule_program;
140pub use reference_eval::{evaluate_condition, evaluate_formula, RuleEvaluationContext};
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn file_size_condition_family_builds_unique_programs() {
148 let programs = [
149 (file_size_eq::OP_ID, file_size_eq::FileSizeEq::program()),
150 (file_size_gt::OP_ID, file_size_gt::FileSizeGt::program()),
151 (file_size_gte::OP_ID, file_size_gte::FileSizeGte::program()),
152 (file_size_lt::OP_ID, file_size_lt::FileSizeLt::program()),
153 (file_size_lte::OP_ID, file_size_lte::FileSizeLte::program()),
154 (file_size_ne::OP_ID, file_size_ne::FileSizeNe::program()),
155 ];
156 for (op_id, program) in programs {
157 assert!(
158 !program.entry().is_empty(),
159 "Fix: generated file-size condition `{op_id}` must emit a non-empty rule program"
160 );
161 assert!(
162 program
163 .entry()
164 .iter()
165 .any(|node| format!("{node:?}").contains(op_id)),
166 "Fix: generated file-size condition `{op_id}` must preserve its op id in the IR"
167 );
168 }
169 }
170
171 #[test]
172 fn file_size_condition_family_uses_rule_predicate_contract() {
173 assert_eq!(
174 file_size_eq::CONTRACT.cost_hint,
175 crate::contracts::RULE_PREDICATE_CHEAP.cost_hint
176 );
177 assert_eq!(
178 file_size_ne::CONTRACT.determinism,
179 crate::contracts::RULE_PREDICATE_CHEAP.determinism
180 );
181 }
182
183 #[test]
184 fn pattern_count_condition_family_builds_unique_programs() {
185 let programs = [
186 (
187 pattern_count_gt::OP_ID,
188 pattern_count_gt::PatternCountGt::program(),
189 ),
190 (
191 pattern_count_gte::OP_ID,
192 pattern_count_gte::PatternCountGte::program(),
193 ),
194 ];
195 for (op_id, program) in programs {
196 assert!(
197 !program.entry().is_empty(),
198 "Fix: generated pattern-count condition `{op_id}` must emit a non-empty rule program"
199 );
200 assert!(
201 program
202 .entry()
203 .iter()
204 .any(|node| format!("{node:?}").contains(op_id)),
205 "Fix: generated pattern-count condition `{op_id}` must preserve its op id in the IR"
206 );
207 }
208 }
209
210 #[test]
211 fn pattern_count_condition_family_uses_rule_predicate_contract() {
212 assert_eq!(
213 pattern_count_gt::CONTRACT.cost_hint,
214 crate::contracts::RULE_PREDICATE_CHEAP.cost_hint
215 );
216 assert_eq!(
217 pattern_count_gte::CONTRACT.determinism,
218 crate::contracts::RULE_PREDICATE_CHEAP.determinism
219 );
220 }
221}