Skip to main content

lace_sans_io/logic/
profile.rs

1//! The fixed standard exchange profile's predicate vocabulary.
2//!
3//! Lace-020 record facts, Lace-050 session authority and peer advertisements,
4//! and the Lace-040 exported facets are declared once here. Validators, the
5//! facet composer, exchange lowering and bindings read names, arities and
6//! argument kinds from this table. Generated executable predicates are
7//! lowercase compiler names, and ILTP wire fact names are Lace-060 vocabulary;
8//! neither is a profile row.
9
10/// Argument kind of one profile predicate position.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ArgKind {
13    /// A supported record hash.
14    Record,
15    /// Record-reference-shaped text, such as a record-link target.
16    RecordReference,
17    /// Text; a record-reference variable is rejected.
18    Text,
19    /// Any text, including record-reference-shaped values.
20    GenericText,
21    /// A field value: `Text` when the field name is a static synthetic or
22    /// structural name (`:Type`, `:Length`, `Data-Length`, `By`, `Mark`),
23    /// otherwise `GenericText`.
24    FieldValue,
25}
26
27/// What a profile predicate is for.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum Role {
30    /// A supplied base fact. Request-only facts are peer advertisement claims,
31    /// reachable only from `SelectAdvertisedCandidate/1`.
32    Base { request_only: bool },
33    /// An exported facet: a source rule head, never referenced in a body.
34    Facet,
35}
36
37/// One declared predicate of the standard profile; its arity is `kinds.len()`.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub struct ProfilePredicate {
40    pub name: &'static str,
41    pub kinds: &'static [ArgKind],
42    pub role: Role,
43}
44
45impl ProfilePredicate {
46    pub const fn arity(&self) -> usize {
47        self.kinds.len()
48    }
49}
50
51const fn base(name: &'static str, kinds: &'static [ArgKind]) -> ProfilePredicate {
52    ProfilePredicate {
53        name,
54        kinds,
55        role: Role::Base {
56            request_only: false,
57        },
58    }
59}
60
61const fn request_only(name: &'static str, kinds: &'static [ArgKind]) -> ProfilePredicate {
62    ProfilePredicate {
63        name,
64        kinds,
65        role: Role::Base { request_only: true },
66    }
67}
68
69const fn facet(name: &'static str, kinds: &'static [ArgKind]) -> ProfilePredicate {
70    ProfilePredicate {
71        name,
72        kinds,
73        role: Role::Facet,
74    }
75}
76
77use ArgKind::{FieldValue, GenericText, Record, RecordReference, Text};
78
79pub const HAVE: ProfilePredicate = base("Have", &[Record]);
80pub const FIELD: ProfilePredicate = base("Field", &[Record, Text, Text, FieldValue]);
81pub const RECORD_LINK: ProfilePredicate = base(
82    "RecordLink",
83    &[Record, Text, Text, GenericText, RecordReference],
84);
85pub const CONTAINS_RECORD: ProfilePredicate = base("ContainsRecord", &[Record, Record]);
86pub const HERE: ProfilePredicate = base("Here", &[Text]);
87pub const PEER_CONFIDENTIAL: ProfilePredicate = base("PeerConfidential", &[Text]);
88pub const ADVERTISED: ProfilePredicate = request_only("Advertised", &[Record]);
89pub const ADVERTISED_FIELD: ProfilePredicate =
90    request_only("AdvertisedField", &[Record, Text, Text, FieldValue]);
91pub const SELECT_STORED_RECORD: ProfilePredicate = facet("SelectStoredRecord", &[Record]);
92pub const SELECT_ADVERTISED_CANDIDATE: ProfilePredicate =
93    facet("SelectAdvertisedCandidate", &[Record]);
94pub const EXPOSE_RECORD: ProfilePredicate = facet("ExposeRecord", &[Record]);
95pub const TX_DISABLED: ProfilePredicate = facet("TxDisabled", &[]);
96pub const RX_DISABLED: ProfilePredicate = facet("RxDisabled", &[]);
97
98/// The Lace-020 record facts, resolved through origin-aware record views.
99pub const RECORD_FACTS: [ProfilePredicate; 4] = [HAVE, FIELD, RECORD_LINK, CONTAINS_RECORD];
100
101/// Every predicate the standard profile declares.
102pub static STANDARD_PROFILE: [ProfilePredicate; 13] = [
103    HAVE,
104    FIELD,
105    RECORD_LINK,
106    CONTAINS_RECORD,
107    HERE,
108    PEER_CONFIDENTIAL,
109    ADVERTISED,
110    ADVERTISED_FIELD,
111    SELECT_STORED_RECORD,
112    SELECT_ADVERTISED_CANDIDATE,
113    EXPOSE_RECORD,
114    TX_DISABLED,
115    RX_DISABLED,
116];
117
118/// The profile predicate named `name`, at its declared arity.
119pub fn lookup(name: &str) -> Option<&'static ProfilePredicate> {
120    STANDARD_PROFILE
121        .iter()
122        .find(|predicate| predicate.name == name)
123}
124
125/// The Lace-020 record fact named `name`.
126pub fn record_fact(name: &str) -> Option<&'static ProfilePredicate> {
127    lookup(name).filter(|predicate| RECORD_FACTS.contains(predicate))
128}
129
130/// Generated executable outputs read by the interlace runtime. Lowercase
131/// compiler names cannot collide with source helpers, which lowering rescopes.
132pub(crate) const GENERATED_MAY_SEND: &str = "lace_may_send";
133pub(crate) const GENERATED_MAY_REQUEST: &str = "lace_may_request";
134/// Generated retention-sweep output: one policy's windowed stored selection.
135pub(crate) const GENERATED_RETAINED: &str = "lace_retained";