use vize_l0::hash::StableHasher128;
use super::{AmbientInput, CachedArtifact};
use crate::key::sink::MANIFEST_DOMAIN;
use crate::key::{ArtifactKey, KeySink};
use crate::stage::Stage;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct InputSet(u16);
impl InputSet {
pub const EMPTY: Self = Self(0);
#[must_use]
pub fn of(inputs: &[AmbientInput]) -> Self {
let mut set = Self::EMPTY;
for input in inputs {
set.0 |= bit(*input);
}
set
}
#[must_use]
pub const fn contains(self, input: AmbientInput) -> bool {
self.0 & bit(input) != 0
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
pub fn iter(self) -> impl Iterator<Item = AmbientInput> {
AmbientInput::ALL
.into_iter()
.filter(move |input| self.contains(*input))
}
}
const fn bit(input: AmbientInput) -> u16 {
1 << input as u8
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ManifestError {
WrongStage {
artifact: CachedArtifact,
stage: Stage,
},
Undeclared {
artifact: CachedArtifact,
missing: InputSet,
extra: InputSet,
},
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct KeyManifest {
values: [Option<[u8; 16]>; AmbientInput::ALL.len()],
}
const WITH_CONTENT: u8 = 1;
const NO_CONTENT: u8 = 0;
impl KeyManifest {
#[must_use]
pub const fn new() -> Self {
Self {
values: [None; AmbientInput::ALL.len()],
}
}
#[must_use]
pub fn with(mut self, input: AmbientInput, value: &str) -> Self {
self.set(input, value);
self
}
pub fn set(&mut self, input: AmbientInput, value: &str) {
let mut hasher = StableHasher128::new();
hasher.update(value.as_bytes());
if let Some(slot) = self.values.get_mut(input as usize) {
*slot = Some(hasher.digest());
}
}
#[must_use]
pub fn inputs(&self) -> InputSet {
let mut set = InputSet::EMPTY;
for input in AmbientInput::ALL {
if self.values.get(input as usize).is_some_and(Option::is_some) {
set.0 |= bit(input);
}
}
set
}
pub fn check(&self, artifact: CachedArtifact) -> Result<(), ManifestError> {
let declared = InputSet::of(artifact.inputs());
let set = self.inputs();
let missing = InputSet(declared.0 & !set.0);
let extra = InputSet(set.0 & !declared.0);
if missing.is_empty() && extra.is_empty() {
return Ok(());
}
Err(ManifestError::Undeclared {
artifact,
missing,
extra,
})
}
pub fn fingerprint(&self, artifact: CachedArtifact) -> Result<[u8; 16], ManifestError> {
self.check(artifact)?;
let mut sink = KeySink::in_domain(MANIFEST_DOMAIN, Stage::Source, 0, 0);
sink.feed_tag(NO_CONTENT);
self.fold_into(&mut sink, artifact);
Ok(sink.finish().hash())
}
fn fold_into(&self, sink: &mut KeySink, artifact: CachedArtifact) {
sink.feed_str(artifact.name());
for input in AmbientInput::ALL {
if let Some(Some(digest)) = self.values.get(input as usize) {
sink.feed_str(input.name());
sink.feed_digest(digest);
}
}
}
}
impl ArtifactKey {
pub fn with_manifest(
self,
artifact: CachedArtifact,
manifest: &KeyManifest,
) -> Result<Self, ManifestError> {
if !artifact.content_stages().contains(&self.stage) {
return Err(ManifestError::WrongStage {
artifact,
stage: self.stage,
});
}
manifest.check(artifact)?;
let mut sink = KeySink::in_domain(MANIFEST_DOMAIN, self.stage, self.schema_version, 0);
sink.feed_tag(WITH_CONTENT);
sink.feed_digest(&self.hash);
manifest.fold_into(&mut sink, artifact);
Ok(sink.finish())
}
}