Skip to main content

presolve_compiler/
effect_capability.rs

1use crate::{ExecutionBoundary, SemanticType};
2
3/// The version of the compiler-owned effect capability registry schema.
4pub const EFFECT_CAPABILITY_REGISTRY_VERSION: u32 = 1;
5
6/// Stable compiler schema identity for one capability.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub struct CapabilityId(pub &'static str);
9
10/// Stable compiler schema identity for one capability operation.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct CapabilityOperationId(pub &'static str);
13
14/// The externally authored static path that identifies one capability operation.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct StaticCapabilityPath(pub &'static str);
17
18/// The supported top-level effect operation forms.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum CapabilityOperationKind {
21    MemberAssignment,
22    MethodCall,
23}
24
25/// The canonical value shapes accepted by an operation signature.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum CapabilityValueContract {
28    String,
29    SerializableDiagnosticValue,
30}
31
32impl CapabilityValueContract {
33    #[must_use]
34    pub const fn semantic_type(self) -> Option<SemanticType> {
35        match self {
36            Self::String => Some(SemanticType::String),
37            Self::SerializableDiagnosticValue => None,
38        }
39    }
40}
41
42/// The fixed or variadic parameters accepted by one operation.
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum CapabilityParameters {
45    Fixed(&'static [CapabilityValueContract]),
46    Variadic(CapabilityValueContract),
47}
48
49/// All Phase F v1 operations are terminal and return no value.
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum CapabilityResultPolicy {
52    Void,
53}
54
55/// Whether operation arguments require structural serialization.
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum ArgumentSerializationPolicy {
58    None,
59    Structural,
60}
61
62/// Stable runtime-facing lowering identity. F4 declares it but does not lower it.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub struct RuntimeCapabilityLowering(pub &'static str);
65
66/// One immutable signature owned by the compiler registry.
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub struct CapabilitySignature {
69    pub parameters: CapabilityParameters,
70    pub result: CapabilityResultPolicy,
71}
72
73/// One immutable operation exposed by a capability.
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub struct CapabilityOperation {
76    pub id: CapabilityOperationId,
77    pub static_path: StaticCapabilityPath,
78    pub kind: CapabilityOperationKind,
79    pub signature: CapabilitySignature,
80    pub boundary: ExecutionBoundary,
81    pub argument_serialization: ArgumentSerializationPolicy,
82    pub runtime_lowering: RuntimeCapabilityLowering,
83}
84
85/// One immutable compiler-owned capability definition.
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub struct CapabilityDefinition {
88    pub id: CapabilityId,
89    pub root: &'static str,
90    pub boundary: ExecutionBoundary,
91    pub operations: &'static [CapabilityOperation],
92    pub provenance: BuiltinCapabilityProvenance,
93}
94
95/// Provenance for definitions that are part of the compiler schema rather than source input.
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum BuiltinCapabilityProvenance {
98    EffectCapabilityRegistryV1,
99}
100
101const STRING_PARAMETER: &[CapabilityValueContract] = &[CapabilityValueContract::String];
102const STRING_PAIR_PARAMETERS: &[CapabilityValueContract] = &[
103    CapabilityValueContract::String,
104    CapabilityValueContract::String,
105];
106
107const DOCUMENT_OPERATIONS: &[CapabilityOperation] = &[CapabilityOperation {
108    id: CapabilityOperationId("builtin.browser.document.title.assign"),
109    static_path: StaticCapabilityPath("document.title"),
110    kind: CapabilityOperationKind::MemberAssignment,
111    signature: CapabilitySignature {
112        parameters: CapabilityParameters::Fixed(STRING_PARAMETER),
113        result: CapabilityResultPolicy::Void,
114    },
115    boundary: ExecutionBoundary::Client,
116    argument_serialization: ArgumentSerializationPolicy::None,
117    runtime_lowering: RuntimeCapabilityLowering("builtin.browser.document.title.assign"),
118}];
119
120const CONSOLE_OPERATIONS: &[CapabilityOperation] = &[
121    CapabilityOperation {
122        id: CapabilityOperationId("builtin.browser.console.log"),
123        static_path: StaticCapabilityPath("console.log"),
124        kind: CapabilityOperationKind::MethodCall,
125        signature: CapabilitySignature {
126            parameters: CapabilityParameters::Variadic(
127                CapabilityValueContract::SerializableDiagnosticValue,
128            ),
129            result: CapabilityResultPolicy::Void,
130        },
131        boundary: ExecutionBoundary::Client,
132        argument_serialization: ArgumentSerializationPolicy::Structural,
133        runtime_lowering: RuntimeCapabilityLowering("builtin.browser.console.log"),
134    },
135    CapabilityOperation {
136        id: CapabilityOperationId("builtin.browser.console.info"),
137        static_path: StaticCapabilityPath("console.info"),
138        kind: CapabilityOperationKind::MethodCall,
139        signature: CapabilitySignature {
140            parameters: CapabilityParameters::Variadic(
141                CapabilityValueContract::SerializableDiagnosticValue,
142            ),
143            result: CapabilityResultPolicy::Void,
144        },
145        boundary: ExecutionBoundary::Client,
146        argument_serialization: ArgumentSerializationPolicy::Structural,
147        runtime_lowering: RuntimeCapabilityLowering("builtin.browser.console.info"),
148    },
149    CapabilityOperation {
150        id: CapabilityOperationId("builtin.browser.console.warn"),
151        static_path: StaticCapabilityPath("console.warn"),
152        kind: CapabilityOperationKind::MethodCall,
153        signature: CapabilitySignature {
154            parameters: CapabilityParameters::Variadic(
155                CapabilityValueContract::SerializableDiagnosticValue,
156            ),
157            result: CapabilityResultPolicy::Void,
158        },
159        boundary: ExecutionBoundary::Client,
160        argument_serialization: ArgumentSerializationPolicy::Structural,
161        runtime_lowering: RuntimeCapabilityLowering("builtin.browser.console.warn"),
162    },
163    CapabilityOperation {
164        id: CapabilityOperationId("builtin.browser.console.error"),
165        static_path: StaticCapabilityPath("console.error"),
166        kind: CapabilityOperationKind::MethodCall,
167        signature: CapabilitySignature {
168            parameters: CapabilityParameters::Variadic(
169                CapabilityValueContract::SerializableDiagnosticValue,
170            ),
171            result: CapabilityResultPolicy::Void,
172        },
173        boundary: ExecutionBoundary::Client,
174        argument_serialization: ArgumentSerializationPolicy::Structural,
175        runtime_lowering: RuntimeCapabilityLowering("builtin.browser.console.error"),
176    },
177];
178
179const LOCAL_STORAGE_OPERATIONS: &[CapabilityOperation] = &[
180    CapabilityOperation {
181        id: CapabilityOperationId("builtin.browser.local_storage.set_item"),
182        static_path: StaticCapabilityPath("localStorage.setItem"),
183        kind: CapabilityOperationKind::MethodCall,
184        signature: CapabilitySignature {
185            parameters: CapabilityParameters::Fixed(STRING_PAIR_PARAMETERS),
186            result: CapabilityResultPolicy::Void,
187        },
188        boundary: ExecutionBoundary::Client,
189        argument_serialization: ArgumentSerializationPolicy::None,
190        runtime_lowering: RuntimeCapabilityLowering("builtin.browser.local_storage.set_item"),
191    },
192    CapabilityOperation {
193        id: CapabilityOperationId("builtin.browser.local_storage.remove_item"),
194        static_path: StaticCapabilityPath("localStorage.removeItem"),
195        kind: CapabilityOperationKind::MethodCall,
196        signature: CapabilitySignature {
197            parameters: CapabilityParameters::Fixed(STRING_PARAMETER),
198            result: CapabilityResultPolicy::Void,
199        },
200        boundary: ExecutionBoundary::Client,
201        argument_serialization: ArgumentSerializationPolicy::None,
202        runtime_lowering: RuntimeCapabilityLowering("builtin.browser.local_storage.remove_item"),
203    },
204];
205
206const SESSION_STORAGE_OPERATIONS: &[CapabilityOperation] = &[
207    CapabilityOperation {
208        id: CapabilityOperationId("builtin.browser.session_storage.set_item"),
209        static_path: StaticCapabilityPath("sessionStorage.setItem"),
210        kind: CapabilityOperationKind::MethodCall,
211        signature: CapabilitySignature {
212            parameters: CapabilityParameters::Fixed(STRING_PAIR_PARAMETERS),
213            result: CapabilityResultPolicy::Void,
214        },
215        boundary: ExecutionBoundary::Client,
216        argument_serialization: ArgumentSerializationPolicy::None,
217        runtime_lowering: RuntimeCapabilityLowering("builtin.browser.session_storage.set_item"),
218    },
219    CapabilityOperation {
220        id: CapabilityOperationId("builtin.browser.session_storage.remove_item"),
221        static_path: StaticCapabilityPath("sessionStorage.removeItem"),
222        kind: CapabilityOperationKind::MethodCall,
223        signature: CapabilitySignature {
224            parameters: CapabilityParameters::Fixed(STRING_PARAMETER),
225            result: CapabilityResultPolicy::Void,
226        },
227        boundary: ExecutionBoundary::Client,
228        argument_serialization: ArgumentSerializationPolicy::None,
229        runtime_lowering: RuntimeCapabilityLowering("builtin.browser.session_storage.remove_item"),
230    },
231];
232
233const DEFINITIONS: &[CapabilityDefinition] = &[
234    CapabilityDefinition {
235        id: CapabilityId("builtin.browser.document"),
236        root: "document",
237        boundary: ExecutionBoundary::Client,
238        operations: DOCUMENT_OPERATIONS,
239        provenance: BuiltinCapabilityProvenance::EffectCapabilityRegistryV1,
240    },
241    CapabilityDefinition {
242        id: CapabilityId("builtin.browser.console"),
243        root: "console",
244        boundary: ExecutionBoundary::Client,
245        operations: CONSOLE_OPERATIONS,
246        provenance: BuiltinCapabilityProvenance::EffectCapabilityRegistryV1,
247    },
248    CapabilityDefinition {
249        id: CapabilityId("builtin.browser.local_storage"),
250        root: "localStorage",
251        boundary: ExecutionBoundary::Client,
252        operations: LOCAL_STORAGE_OPERATIONS,
253        provenance: BuiltinCapabilityProvenance::EffectCapabilityRegistryV1,
254    },
255    CapabilityDefinition {
256        id: CapabilityId("builtin.browser.session_storage"),
257        root: "sessionStorage",
258        boundary: ExecutionBoundary::Client,
259        operations: SESSION_STORAGE_OPERATIONS,
260        provenance: BuiltinCapabilityProvenance::EffectCapabilityRegistryV1,
261    },
262];
263
264/// The immutable, deterministic built-in registry for Phase F effects.
265#[derive(Debug, Clone, Copy, PartialEq, Eq)]
266pub struct EffectCapabilityRegistry;
267
268impl EffectCapabilityRegistry {
269    #[must_use]
270    pub const fn version(self) -> u32 {
271        EFFECT_CAPABILITY_REGISTRY_VERSION
272    }
273
274    #[must_use]
275    pub const fn definitions(self) -> &'static [CapabilityDefinition] {
276        DEFINITIONS
277    }
278
279    /// Resolves one already-classified canonical operation identity.
280    #[must_use]
281    pub fn operation(self, id: CapabilityOperationId) -> Option<&'static CapabilityOperation> {
282        self.definitions()
283            .iter()
284            .flat_map(|definition| definition.operations)
285            .find(|operation| operation.id == id)
286    }
287
288    #[must_use]
289    pub fn operation_at(
290        self,
291        path: &str,
292        kind: CapabilityOperationKind,
293    ) -> Option<&'static CapabilityOperation> {
294        self.definitions()
295            .iter()
296            .flat_map(|definition| definition.operations)
297            .find(|operation| operation.kind == kind && operation.static_path.0 == path)
298    }
299}
300
301/// The one authoritative registry instance used by all compiler consumers.
302pub const EFFECT_CAPABILITY_REGISTRY: EffectCapabilityRegistry = EffectCapabilityRegistry;
303
304#[cfg(test)]
305mod tests {
306    use super::{
307        CapabilityOperationKind, EFFECT_CAPABILITY_REGISTRY, EFFECT_CAPABILITY_REGISTRY_VERSION,
308    };
309
310    #[test]
311    fn registry_v1_exposes_only_the_initial_exact_static_operations() {
312        let registry = EFFECT_CAPABILITY_REGISTRY;
313        assert_eq!(registry.version(), EFFECT_CAPABILITY_REGISTRY_VERSION);
314        assert_eq!(registry.definitions().len(), 4);
315        assert_eq!(
316            registry
317                .operation_at("document.title", CapabilityOperationKind::MemberAssignment)
318                .expect("document title operation")
319                .id
320                .0,
321            "builtin.browser.document.title.assign"
322        );
323        assert!(registry
324            .operation_at("window.console.log", CapabilityOperationKind::MethodCall)
325            .is_none());
326        assert!(registry
327            .operation_at("analytics.track", CapabilityOperationKind::MethodCall)
328            .is_none());
329    }
330}