lace_sans_io/logic/
policy.rs1use crate::records::RecordHash;
8use std::sync::Arc;
9
10use super::lacegram::{CanonicalLacegram, canonicalize_lacegram_source, prepare_lacegram_rules};
11use super::limits::PreparationError;
12use super::policy_source::validate_request_only_advertisement_dependencies;
13
14#[derive(Clone, Debug)]
16pub struct InterlaceModule {
17 canonical: Arc<CanonicalLacegram>,
18}
19
20impl PartialEq for InterlaceModule {
21 fn eq(&self, other: &Self) -> bool {
22 self.canonical.text == other.canonical.text
23 }
24}
25
26impl Eq for InterlaceModule {}
27
28impl InterlaceModule {
29 pub fn from_text(text: &str) -> Result<Self, PreparationError> {
31 let canonical =
32 canonicalize_lacegram_source(text, super::limits::DatalogLimits::default())?;
33 validate_request_only_advertisement_dependencies(&canonical.program)?;
34 Ok(Self {
35 canonical: Arc::new(canonical),
36 })
37 }
38
39 pub fn canonical_text(&self) -> &str {
41 &self.canonical.text
42 }
43
44 pub fn lacegram_resource_hash(&self) -> RecordHash {
46 super::exchange_plan::lacegram_resource_hash(&self.canonical.text)
47 }
48
49 pub fn from_rules(rules: Vec<crate::logic::Rule>) -> Result<Self, PreparationError> {
51 let canonical = prepare_lacegram_rules(rules, super::limits::DatalogLimits::default())?;
52 validate_request_only_advertisement_dependencies(&canonical.program)?;
53 Ok(Self {
54 canonical: Arc::new(canonical),
55 })
56 }
57
58 pub fn has_facet(&self) -> bool {
61 self.program().strata.iter().flatten().any(|rule| {
62 super::profile::lookup(&rule.head_predicate)
63 .is_some_and(|predicate| predicate.role == super::profile::Role::Facet)
64 })
65 }
66
67 pub fn program(&self) -> &crate::logic::Lacegram {
69 &self.canonical.program
70 }
71
72 pub fn id(&self) -> RecordHash {
73 self.lacegram_resource_hash()
74 }
75}
76
77pub fn all_records() -> &'static InterlaceModule {
79 static MODULE: std::sync::OnceLock<InterlaceModule> = std::sync::OnceLock::new();
80 constant(&MODULE, ALL_RECORDS)
81}
82
83pub fn tx_disabled() -> &'static InterlaceModule {
85 static MODULE: std::sync::OnceLock<InterlaceModule> = std::sync::OnceLock::new();
86 constant(&MODULE, TX_DISABLED)
87}
88
89pub fn rx_disabled() -> &'static InterlaceModule {
91 static MODULE: std::sync::OnceLock<InterlaceModule> = std::sync::OnceLock::new();
92 constant(&MODULE, RX_DISABLED)
93}
94
95const ALL_RECORDS: &str = "ExposeRecord(Record) :- Have(Record).\nSelectAdvertisedCandidate(Record) :- Advertised(Record).\nSelectStoredRecord(Record) :- Have(Record).";
96const TX_DISABLED: &str = "TxDisabled() :- true.";
97const RX_DISABLED: &str = "RxDisabled() :- true.";
98
99fn constant(
100 cell: &'static std::sync::OnceLock<InterlaceModule>,
101 text: &str,
102) -> &'static InterlaceModule {
103 cell.get_or_init(|| InterlaceModule::from_text(text).expect("constant policy is valid"))
104}
105
106#[cfg(test)]
107#[test]
108fn constant_policies_are_canonical() {
109 for (module, text) in [
110 (all_records(), ALL_RECORDS),
111 (tx_disabled(), TX_DISABLED),
112 (rx_disabled(), RX_DISABLED),
113 ] {
114 assert_eq!(module.canonical_text(), text);
115 }
116}