1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024-2026 Dragonscale Team
//! Rule registry facade for managing pre-compiled Locy rules.
use std::sync::Arc;
use uni_common::Result;
use super::impl_locy::LocyRuleRegistry;
/// Metadata about a registered Locy rule.
#[derive(Debug, Clone)]
pub struct RuleInfo {
/// Rule name.
pub name: String,
/// Number of clauses in the rule.
pub clause_count: usize,
/// Whether the rule is recursive.
pub is_recursive: bool,
}
/// Facade for managing pre-compiled Locy rules.
///
/// Obtained via `db.rules()`, `session.rules()`, or `tx.rules()`.
/// Rules registered at the database level are cloned into every new Session.
pub struct RuleRegistry<'a> {
registry: &'a Arc<std::sync::RwLock<LocyRuleRegistry>>,
}
impl<'a> RuleRegistry<'a> {
pub fn new(registry: &'a Arc<std::sync::RwLock<LocyRuleRegistry>>) -> Self {
Self { registry }
}
/// Register Locy rules from a program string.
///
/// The program can contain multiple rule definitions. They are compiled
/// and merged into the registry.
pub fn register(&self, program: &str) -> Result<()> {
super::impl_locy::register_rules_on_registry(self.registry, program)
}
/// Remove a rule by name.
///
/// Returns `true` if the rule was found and removed, `false` if it didn't exist.
/// Recompiles all remaining rules from source to rebuild strata ordering.
pub fn remove(&self, name: &str) -> Result<bool> {
let mut registry = self.registry.write().unwrap();
if !registry.rules.contains_key(name) {
return Ok(false);
}
// Save sources, then clear everything for recompilation
let sources = registry.sources.clone();
registry.rules.clear();
registry.strata.clear();
registry.sources.clear();
drop(registry);
// Re-register each source program, then remove the target rule from the result
for source in &sources {
let _ = super::impl_locy::register_rules_on_registry(self.registry, source);
}
// Remove the target rule from the compiled output
let mut registry = self.registry.write().unwrap();
registry.rules.remove(name);
Ok(true)
}
/// List names of all registered rules.
pub fn list(&self) -> Vec<String> {
let registry = self.registry.read().unwrap();
let mut names: Vec<String> = registry.rules.keys().cloned().collect();
names.sort();
names
}
/// Get metadata about a registered rule.
pub fn get(&self, name: &str) -> Option<RuleInfo> {
let registry = self.registry.read().unwrap();
registry.rules.get(name).map(|rule| {
// Check if this rule appears in a recursive stratum
let is_recursive = registry
.strata
.iter()
.any(|s| s.is_recursive && s.rules.iter().any(|r| r.name == name));
RuleInfo {
name: name.to_string(),
clause_count: rule.clauses.len(),
is_recursive,
}
})
}
/// Clear all registered rules.
pub fn clear(&self) {
let mut registry = self.registry.write().unwrap();
registry.rules.clear();
registry.strata.clear();
registry.sources.clear();
}
/// Get the number of registered rules.
pub fn count(&self) -> usize {
let registry = self.registry.read().unwrap();
registry.rules.len()
}
/// Clone the underlying registry Arc for use in Python bindings.
///
/// This allows creating a `PyRuleRegistry` that outlives the borrow
/// of `RuleRegistry<'a>`.
pub fn clone_registry_arc(&self) -> Arc<std::sync::RwLock<LocyRuleRegistry>> {
self.registry.clone()
}
}