1
//! ### A decentralised rule registry for Conjure Oxide
2
//!
3
//! This crate allows registering valid functions as expression-reduction rules.
4
//! Functions can be decorated with the `register_rule` macro in any downstream crate and be used by Conjure Oxide's rule engine.
5
//! To achieve compile-time linking, we make use of the [`linkme`](https://docs.rs/linkme/latest/linkme/) crate.
6
//!
7

            
8
// Why all the re-exports and wierdness?
9
// ============================
10
//
11
// Procedural macros are unhygenic - they directly subsitute into source code, and do not have
12
// their own scope, imports, and so on.
13
//
14
// See [https://doc.rust-lang.org/reference/procedural-macros.html#procedural-macro-hygiene].
15
//
16
// Therefore, we cannot assume the user has any dependencies apart from the one they imported the
17
// macro from. (Also, note Rust does not bring transitive dependencies into scope, so we cannot
18
// assume the presence of a dependency of the crate.)
19
//
20
// To solve this, the crate the macro is in must re-export everything the macro needs to run.
21
//
22
// However, proc-macro crates can only export proc-macros. Therefore, we must use a "front end
23
// crate" (i.e. this one) to re-export both the macro and all the things it may need.
24

            
25
use conjure_core::rule::Rule;
26
use linkme::distributed_slice;
27

            
28
#[doc(hidden)]
29
pub mod _dependencies {
30
    pub use conjure_core::rule::Rule;
31
    pub use linkme::distributed_slice;
32
}
33

            
34
#[doc(hidden)]
35
#[distributed_slice]
36
pub static RULES_DISTRIBUTED_SLICE: [Rule<'static>];
37

            
38
/// Returns a copied `Vec` of all rules registered with the `register_rule` macro.
39
///
40
/// Rules are not guaranteed to be in any particular order.
41
///
42
/// # Example
43
/// ```rust
44
/// # use conjure_rules::register_rule;
45
/// # use conjure_core::rule::{Rule, RuleApplicationError};
46
/// # use conjure_core::ast::Expression;
47
/// #
48
/// #[register_rule]
49
/// fn identity(expr: &Expression) -> Result<Expression, RuleApplicationError> {
50
///   Ok(expr.clone())
51
/// }
52
///
53
/// fn main() {
54
///   println!("Rules: {:?}", conjure_rules::get_rules());
55
/// }
56
/// ```
57
///
58
/// This will print (if no other rules are registered):
59
/// ```text
60
///   Rules: [Rule { name: "identity", application: MEM }]
61
/// ```
62
/// Where `MEM` is the memory address of the `identity` function.
63
pub fn get_rules() -> Vec<Rule<'static>> {
64
    RULES_DISTRIBUTED_SLICE.to_vec()
65
}
66

            
67
pub fn get_rule_by_name(name: &str) -> Option<Rule<'static>> {
68
    get_rules().iter().find(|rule| rule.name == name).cloned()
69
}
70

            
71
/// This procedural macro registers a decorated function with `conjure_rules`' global registry.
72
/// It may be used in any downstream crate. For more information on linker magic, see the [`linkme`](https://docs.rs/linkme/latest/linkme/) crate.
73
///
74
/// **IMPORTANT**: Since the resulting rule may not be explicitly referenced, it may be removed by the compiler's dead code elimination.
75
/// To prevent this, you must ensure that either:
76
/// 1. codegen-units is set to 1, i.e. in Cargo.toml:
77
/// ```toml
78
/// [profile.release]
79
/// codegen-units = 1
80
/// ```
81
/// 2. The function is included somewhere else in the code
82
///
83
/// <hr>
84
///
85
/// Functions must have the signature `fn(&Expr) -> Result<Expr, RuleApplicationError>`.
86
/// The created rule will have the same name as the function.
87
///
88
/// Intermediary static variables are created to allow for the decentralized registry, with the prefix `CONJURE_GEN_`.
89
/// Please ensure that other variable names in the same scope do not conflict with these.
90
///
91
/// <hr>
92
///
93
/// For example:
94
/// ```rust
95
/// # use conjure_core::ast::Expression;
96
/// # use conjure_core::rule::RuleApplicationError;
97
/// # use conjure_rules::register_rule;
98
/// #
99
/// #[register_rule]
100
/// fn identity(expr: &Expression) -> Result<Expression, RuleApplicationError> {
101
///   Ok(expr.clone())
102
/// }
103
/// ```
104
#[doc(inline)]
105
pub use conjure_rules_proc_macro::register_rule;