Skip to main content

a3s_code_core/capability/
source.rs

1use serde::Serialize;
2
3use super::{
4    CapabilitySetError, CapabilitySourceId, Sha256Digest, UseCapabilityGeneration,
5    UsePackageGeneration,
6};
7
8/// Trusted precedence class assigned by the host adapter, never by package
9/// content.
10#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
11#[serde(rename_all = "kebab-case")]
12pub enum CapabilitySourceClass {
13    BuiltIn,
14    Host,
15    UsePackage,
16    Session,
17}
18
19#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
20#[serde(rename_all = "camelCase", tag = "kind")]
21enum CapabilitySourceEvidence {
22    // CAP-SET1 seals construction inside Core. Surface projection will become
23    // the first production constructor consumer in CAP-PROJ1.
24    #[allow(dead_code)]
25    BuiltIn {
26        revision: Sha256Digest,
27    },
28    Host {
29        revision: Sha256Digest,
30    },
31    UsePackage {
32        capability: UseCapabilityGeneration,
33        package: UsePackageGeneration,
34    },
35    Session {
36        revision: Sha256Digest,
37    },
38}
39
40/// Immutable identity and authority class for one complete contribution.
41/// Fields are private so untrusted package data cannot manufacture built-in
42/// precedence.
43#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
44#[serde(rename_all = "camelCase")]
45pub struct CapabilitySource {
46    id: CapabilitySourceId,
47    class: CapabilitySourceClass,
48    evidence: CapabilitySourceEvidence,
49}
50
51impl CapabilitySource {
52    #[allow(dead_code)]
53    pub(crate) fn builtin(
54        component: impl Into<String>,
55        revision: Sha256Digest,
56    ) -> Result<Self, CapabilitySetError> {
57        Ok(Self {
58            id: CapabilitySourceId::scoped("builtin", component)?,
59            class: CapabilitySourceClass::BuiltIn,
60            evidence: CapabilitySourceEvidence::BuiltIn { revision },
61        })
62    }
63
64    pub fn host(
65        component: impl Into<String>,
66        revision: Sha256Digest,
67    ) -> Result<Self, CapabilitySetError> {
68        Ok(Self {
69            id: CapabilitySourceId::scoped("host", component)?,
70            class: CapabilitySourceClass::Host,
71            evidence: CapabilitySourceEvidence::Host { revision },
72        })
73    }
74
75    pub fn session(
76        registration: impl Into<String>,
77        revision: Sha256Digest,
78    ) -> Result<Self, CapabilitySetError> {
79        Ok(Self {
80            id: CapabilitySourceId::scoped("session", registration)?,
81            class: CapabilitySourceClass::Session,
82            evidence: CapabilitySourceEvidence::Session { revision },
83        })
84    }
85
86    pub fn use_package(
87        capability: UseCapabilityGeneration,
88        package: UsePackageGeneration,
89    ) -> Result<Self, CapabilitySetError> {
90        if capability.generation() == 0 {
91            return Err(CapabilitySetError::InvalidGeneration {
92                field: "use_capability_generation",
93            });
94        }
95        let id = CapabilitySourceId::scoped("use", package.package_id())?;
96        Ok(Self {
97            id,
98            class: CapabilitySourceClass::UsePackage,
99            evidence: CapabilitySourceEvidence::UsePackage {
100                capability,
101                package,
102            },
103        })
104    }
105
106    pub fn id(&self) -> &CapabilitySourceId {
107        &self.id
108    }
109
110    pub const fn class(&self) -> CapabilitySourceClass {
111        self.class
112    }
113
114    pub fn revision(&self) -> Option<&Sha256Digest> {
115        match &self.evidence {
116            CapabilitySourceEvidence::BuiltIn { revision }
117            | CapabilitySourceEvidence::Host { revision }
118            | CapabilitySourceEvidence::Session { revision } => Some(revision),
119            CapabilitySourceEvidence::UsePackage { .. } => None,
120        }
121    }
122
123    pub fn use_capability_generation(&self) -> Option<&UseCapabilityGeneration> {
124        match &self.evidence {
125            CapabilitySourceEvidence::UsePackage { capability, .. } => Some(capability),
126            _ => None,
127        }
128    }
129
130    pub fn use_package_generation(&self) -> Option<&UsePackageGeneration> {
131        match &self.evidence {
132            CapabilitySourceEvidence::UsePackage { package, .. } => Some(package),
133            _ => None,
134        }
135    }
136}