Skip to main content

runmat_execution_artifact/bundle/
closure.rs

1use runmat_execution::Digest;
2use runmat_package::FrozenProjectHandoff;
3use serde::{Deserialize, Serialize};
4
5use crate::{ArtifactError, ArtifactResult};
6
7#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
8#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
9pub enum BundleCodeClosure {
10    SourceProject { handoff: FrozenProjectHandoff },
11    Compiled { package: CompiledPackageClosure },
12}
13
14#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
15#[serde(deny_unknown_fields)]
16pub struct CompiledPackageClosure {
17    pub schema_version: u16,
18    pub graph_digest: Digest,
19    pub source_digest: Digest,
20    pub package_instances: Vec<String>,
21}
22
23impl CompiledPackageClosure {
24    pub const SCHEMA_VERSION: u16 = 1;
25
26    pub fn validate(&self) -> ArtifactResult<()> {
27        if self.schema_version != Self::SCHEMA_VERSION
28            || self.package_instances.is_empty()
29            || self
30                .package_instances
31                .windows(2)
32                .any(|pair| pair[0] >= pair[1])
33            || self
34                .package_instances
35                .iter()
36                .any(|identity| !valid_identity(identity))
37        {
38            return Err(ArtifactError::Invalid(
39                "compiled package closure is not canonical".into(),
40            ));
41        }
42        Ok(())
43    }
44}
45
46fn valid_identity(value: &str) -> bool {
47    !value.is_empty()
48        && value.len() <= 512
49        && value.is_ascii()
50        && !value.chars().any(char::is_control)
51}