Skip to main content

presolve_compiler/
semantic_package_runtime.rs

1//! Explicit host-owned locations for semantic-package runtime modules.
2
3use std::collections::BTreeMap;
4
5use crate::SemanticPackageContract;
6
7#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
8pub struct SemanticPackageRuntimeModuleKey {
9    pub package: String,
10    pub version: String,
11    pub integrity: String,
12    pub runtime_module: String,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum SemanticPackageRuntimeModuleError {
17    EmptyLocation,
18    DuplicateRuntimeModule,
19    ContractMismatch,
20}
21
22/// The application/metaframework-provided location of exact package runtime
23/// bytes. This table is a compiler input; it performs no package discovery.
24#[derive(Debug, Clone, PartialEq, Eq, Default)]
25pub struct SemanticPackageRuntimeModuleTable {
26    modules: BTreeMap<SemanticPackageRuntimeModuleKey, String>,
27}
28
29impl SemanticPackageRuntimeModuleTable {
30    pub fn insert(
31        &mut self,
32        key: SemanticPackageRuntimeModuleKey,
33        location: String,
34    ) -> Result<(), SemanticPackageRuntimeModuleError> {
35        if location.is_empty() {
36            return Err(SemanticPackageRuntimeModuleError::EmptyLocation);
37        }
38        if self.modules.contains_key(&key) {
39            return Err(SemanticPackageRuntimeModuleError::DuplicateRuntimeModule);
40        }
41        self.modules.insert(key, location);
42        Ok(())
43    }
44
45    #[must_use]
46    pub fn resolve(&self, key: &SemanticPackageRuntimeModuleKey) -> Option<&str> {
47        self.modules.get(key).map(String::as_str)
48    }
49
50    #[must_use]
51    pub fn contains(&self, key: &SemanticPackageRuntimeModuleKey) -> bool {
52        self.modules.contains_key(key)
53    }
54
55    pub fn resolve_contract_module(
56        &self,
57        contract: &SemanticPackageContract,
58        runtime_module: &str,
59    ) -> Result<&str, SemanticPackageRuntimeModuleError> {
60        let key = SemanticPackageRuntimeModuleKey {
61            package: contract.package.clone(),
62            version: contract.version.clone(),
63            integrity: contract.integrity.clone(),
64            runtime_module: runtime_module.to_string(),
65        };
66        self.resolve(&key)
67            .ok_or(SemanticPackageRuntimeModuleError::ContractMismatch)
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use crate::{
74        parse_semantic_package_contract, SemanticPackageRuntimeModuleError,
75        SemanticPackageRuntimeModuleKey, SemanticPackageRuntimeModuleTable,
76    };
77
78    #[test]
79    fn resolves_only_the_exact_integrity_checked_runtime_module_coordinate() {
80        let contract = parse_semantic_package_contract(
81            r#"{"schema_version":1,"package":"profile-service","version":"1.2.3","integrity":"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","exports":{"loadProfile":{"kind":"resource","type_signature":"() -> Resource<string, string>","runtime_module":"dist/load-profile.js","resume_policy":"snapshot","resource_endpoint":{"execution_boundary":"shared","cancellation":"abort","resume":"snapshot"}}}}"#,
82        )
83        .unwrap();
84        let mut modules = SemanticPackageRuntimeModuleTable::default();
85        modules
86            .insert(
87                SemanticPackageRuntimeModuleKey {
88                    package: "profile-service".into(),
89                    version: "1.2.3".into(),
90                    integrity:
91                        "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
92                            .into(),
93                    runtime_module: "dist/load-profile.js".into(),
94                },
95                "./vendor/profile-service.js".into(),
96            )
97            .unwrap();
98        assert_eq!(
99            modules
100                .resolve_contract_module(&contract, "dist/load-profile.js")
101                .unwrap(),
102            "./vendor/profile-service.js"
103        );
104        assert_eq!(
105            modules.resolve_contract_module(&contract, "other.js"),
106            Err(SemanticPackageRuntimeModuleError::ContractMismatch)
107        );
108    }
109}