1use sha2::{Digest, Sha256};
10
11use crate::{
12 artifact_tree_payload, canonicalize_json_bytes, framed_preimage, framed_tuple_payload,
13 identity_metadata, AnyHashId, ArtifactEntryKind, ArtifactTreeEntry, CanonicalizationProfile,
14 HashError, HashKindName, TupleField,
15};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub enum VectorInput {
20 RawBytes(Vec<u8>),
22 TupleFields(Vec<(String, Vec<u8>)>),
24 TreeEntries(Vec<VectorTreeEntry>),
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct VectorTreeEntry {
30 pub path: String,
31 pub bytes: Vec<u8>,
32 pub symlink: bool,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct VectorOutcome {
37 pub canonical_payload: Vec<u8>,
38 pub preimage: Vec<u8>,
39 pub digest: [u8; 32],
40 pub hash_id: AnyHashId,
41}
42
43pub fn execute_vector(
47 kind: HashKindName,
48 profile: CanonicalizationProfile,
49 input: &VectorInput,
50) -> Result<VectorOutcome, HashError> {
51 let metadata = identity_metadata(kind);
52 if metadata.profile != profile {
53 return Err(HashError::ProfileMismatch {
54 kind: kind.to_string(),
55 expected: metadata.profile.to_string(),
56 actual: profile.to_string(),
57 });
58 }
59
60 let canonical_payload = match (profile, input) {
61 (CanonicalizationProfile::RawBytesV1, VectorInput::RawBytes(bytes)) => bytes.clone(),
62 (CanonicalizationProfile::AreteJcsV1, VectorInput::RawBytes(bytes)) => {
63 canonicalize_json_bytes(bytes)?
64 }
65 (CanonicalizationProfile::FramedTupleV1, VectorInput::TupleFields(fields)) => {
66 let fields: Vec<TupleField<'_>> = fields
67 .iter()
68 .map(|(label, value)| TupleField::new(label, value))
69 .collect();
70 framed_tuple_payload(&fields)?
71 }
72 (CanonicalizationProfile::ArtifactTreeV1, VectorInput::TreeEntries(entries)) => {
73 let entries: Vec<ArtifactTreeEntry<'_>> = entries
74 .iter()
75 .map(|entry| ArtifactTreeEntry {
76 path: &entry.path,
77 bytes: &entry.bytes,
78 kind: if entry.symlink {
79 ArtifactEntryKind::Symlink
80 } else {
81 ArtifactEntryKind::File
82 },
83 })
84 .collect();
85 artifact_tree_payload(&entries)?
86 }
87 _ => {
88 return Err(HashError::InvalidHashId(
89 "vector input encoding does not match the canonicalization profile",
90 ))
91 }
92 };
93
94 let preimage = framed_preimage(kind, profile, &canonical_payload);
95 let digest: [u8; 32] = Sha256::digest(&preimage).into();
96 Ok(VectorOutcome {
97 canonical_payload,
98 preimage,
99 digest,
100 hash_id: AnyHashId::from_parts(kind, digest),
101 })
102}