1
pub use linkme::distributed_slice;
2

            
3
/// This procedural macro registers a decorated function with `conjure_rules`' global registry, and
4
/// adds the rule to one or more `RuleSet`'s.
5
///
6
/// It may be used in any downstream crate.
7
/// For more information on linker magic, see the [`linkme`](https://docs.rs/linkme/latest/linkme/) crate.
8
///
9
/// **IMPORTANT**: Since the resulting rule may not be explicitly referenced, it may be removed by the compiler's dead code elimination.
10
/// To prevent this, you must ensure that either:
11
/// 1. codegen-units is set to 1, i.e. in Cargo.toml:
12
/// ```toml
13
/// [profile.release]
14
/// codegen-units = 1
15
/// ```
16
/// 2. The function is included somewhere else in the code
17
///
18
/// <hr>
19
///
20
/// Functions must have the signature `fn(&Expr) -> ApplicationResult`.
21
/// The created rule will have the same name as the function.
22
///
23
/// Intermediary static variables are created to allow for the decentralized registry, with the prefix `CONJURE_GEN_`.
24
/// Please ensure that other variable names in the same scope do not conflict with these.
25
///
26
/// This macro must decorate a function with the given signature.
27
/// As arguments, it excepts a tuple of 2-tuples in the format:
28
/// `((<RuleSet name>, <Priority in RuleSet>), ...)`
29
///
30
/// <hr>
31
///
32
/// For example:
33
/// ```rust
34
/// use conjure_core::ast::Expression;
35
/// use conjure_core::model::Model;
36
/// use conjure_core::rule_engine::{ApplicationError, ApplicationResult, Reduction};
37
/// use conjure_core::rule_engine::register_rule;
38
///
39
/// #[register_rule(("RuleSetName", 10))]
40
/// fn identity(expr: &Expression, mdl: &Model) -> ApplicationResult {
41
///   Ok(Reduction::pure(expr.clone()))
42
/// }
43
/// ```
44
pub use conjure_macros::register_rule;
45

            
46
/// This procedural macro registers a rule set with the global registry.
47
/// It may be used in any downstream crate.
48
///
49
/// For more information on linker magic, see the [`linkme`](https://docs.rs/linkme/latest/linkme/) crate.
50
///
51
/// This macro uses the following syntax:
52
///
53
/// ```text
54
/// register_rule_set!(<RuleSet name>, <RuleSet order>, (<DependencyRuleSet1>, <DependencyRuleSet2>, ...));
55
/// ```
56
///
57
/// # Example
58
///
59
/// ```rust
60
/// use conjure_core::rule_engine::register_rule_set;
61
///
62
/// register_rule_set!("MyRuleSet", 10, ("DependencyRuleSet", "AnotherRuleSet"));
63
/// ```
64
#[doc(inline)]
65
pub use conjure_macros::register_rule_set;
66
pub use resolve_rules::{get_rule_priorities, get_rules_vec, resolve_rule_sets};
67
pub use rewrite::{rewrite_model, RewriteError};
68
pub use rule::{ApplicationError, ApplicationResult, Reduction, Rule};
69
pub use rule_set::RuleSet;
70

            
71
use crate::solver::SolverFamily;
72

            
73
mod resolve_rules;
74
mod rewrite;
75
mod rule;
76
mod rule_set;
77

            
78
#[doc(hidden)]
79
#[distributed_slice]
80
pub static RULES_DISTRIBUTED_SLICE: [Rule<'static>];
81

            
82
#[doc(hidden)]
83
#[distributed_slice]
84
pub static RULE_SETS_DISTRIBUTED_SLICE: [RuleSet<'static>];
85

            
86
pub mod _dependencies {
87
    pub use linkme;
88
    pub use linkme::distributed_slice;
89
}
90

            
91
/// Returns a copied `Vec` of all rules registered with the `register_rule` macro.
92
///
93
/// Rules are not guaranteed to be in any particular order.
94
///
95
/// # Example
96
/// ```rust
97
/// # use conjure_core::rule_engine::{ApplicationResult, Reduction, get_rules};
98
/// # use conjure_core::ast::Expression;
99
/// # use conjure_core::model::Model;
100
/// # use conjure_core::rule_engine::register_rule;
101
///
102
/// #[register_rule]
103
/// fn identity(expr: &Expression, mdl: &Model) -> ApplicationResult {
104
///   Ok(Reduction::pure(expr.clone()))
105
/// }
106
///
107
/// fn main() {
108
///   println!("Rules: {:?}", get_rules());
109
/// }
110
/// ```
111
///
112
/// This will print (if no other rules are registered):
113
/// ```text
114
///   Rules: [Rule { name: "identity", application: MEM }]
115
/// ```
116
/// Where `MEM` is the memory address of the `identity` function.
117
pub fn get_rules() -> Vec<&'static Rule<'static>> {
118
    RULES_DISTRIBUTED_SLICE.iter().collect()
119
}
120

            
121
/// Get a rule by name.
122
/// Returns the rule with the given name or None if it doesn't exist.
123
///
124
/// # Example
125
/// ```rust
126
/// use conjure_core::rule_engine::register_rule;
127
/// use conjure_core::rule_engine::{Rule, ApplicationResult, Reduction, get_rule_by_name};
128
/// use conjure_core::ast::Expression;
129
/// use conjure_core::model::Model;
130
///
131
/// #[register_rule]
132
/// fn identity(expr: &Expression, mdl: &Model) -> ApplicationResult {
133
///  Ok(Reduction::pure(expr.clone()))
134
/// }
135
///
136
/// fn main() {
137
/// println!("Rule: {:?}", get_rule_by_name("identity"));
138
/// }
139
/// ```
140
///
141
/// This will print:
142
/// ```text
143
/// Rule: Some(Rule { name: "identity", application: MEM })
144
/// ```
145
pub fn get_rule_by_name(name: &str) -> Option<&'static Rule<'static>> {
146
    get_rules().iter().find(|rule| rule.name == name).cloned()
147
}
148

            
149
/// Get all rule sets
150
/// Returns a `Vec` of static references to all rule sets registered with the `register_rule_set` macro.
151
/// Rule sets are not guaranteed to be in any particular order.
152
///
153
/// # Example
154
/// ```rust
155
/// use conjure_core::rule_engine::register_rule_set;
156
/// use conjure_core::rule_engine::get_rule_sets;
157
///
158
/// register_rule_set!("MyRuleSet", 10, ("AnotherRuleSet"));
159
/// register_rule_set!("AnotherRuleSet", 5, ());
160
///
161
/// println!("Rule sets: {:?}", get_rule_sets());
162
/// ```
163
///
164
/// This will print (if no other rule sets are registered):
165
/// ```text
166
/// Rule sets: [
167
///   RuleSet { name: "MyRuleSet", order: 10, rules: OnceLock { state: Uninitialized }, dependencies: ["AnotherRuleSet"] },
168
///   RuleSet { name: "AnotherRuleSet", order: 5, rules: OnceLock { state: Uninitialized }, dependencies: [] }
169
/// ]
170
/// ```
171
///
172
pub fn get_rule_sets() -> Vec<&'static RuleSet<'static>> {
173
    RULE_SETS_DISTRIBUTED_SLICE.iter().collect()
174
}
175

            
176
/// Get a rule set by name.
177
/// Returns the rule set with the given name or None if it doesn't exist.
178
///
179
/// # Example
180
/// ```rust
181
/// use conjure_core::rule_engine::register_rule_set;
182
/// use conjure_core::rule_engine::get_rule_set_by_name;
183
///
184
/// register_rule_set!("MyRuleSet", 10, ("DependencyRuleSet", "AnotherRuleSet"));
185
///
186
/// println!("Rule set: {:?}", get_rule_set_by_name("MyRuleSet"));
187
/// ```
188
///
189
/// This will print:
190
/// ```text
191
/// Rule set: Some(RuleSet { name: "MyRuleSet", order: 10, rules: OnceLock { state: Uninitialized }, dependencies: ["DependencyRuleSet", "AnotherRuleSet"] })
192
/// ```
193
pub fn get_rule_set_by_name(name: &str) -> Option<&'static RuleSet<'static>> {
194
    get_rule_sets()
195
        .iter()
196
        .find(|rule_set| rule_set.name == name)
197
        .cloned()
198
}
199

            
200
/// Get all rule sets for a given solver family.
201
/// Returns a `Vec` of static references to all rule sets that are applicable to the given solver family.
202
///
203
/// # Example
204
///
205
/// ```rust
206
/// use conjure_core::solver::SolverFamily;
207
/// use conjure_core::rule_engine::get_rule_sets_for_solver_family;
208
///
209
/// let rule_sets = get_rule_sets_for_solver_family(SolverFamily::SAT);
210
/// assert_eq!(rule_sets.len(), 1);
211
/// assert_eq!(rule_sets[0].name, "CNF");
212
/// ```
213
pub fn get_rule_sets_for_solver_family(
214
    solver_family: SolverFamily,
215
) -> Vec<&'static RuleSet<'static>> {
216
    get_rule_sets()
217
        .iter()
218
        .filter(|rule_set| {
219
            rule_set
220
                .solver_families
221
                .iter()
222
                .any(|family| family.eq(&solver_family))
223
        })
224
        .cloned()
225
        .collect()
226
}