byteor_pipeline_spec/
canonical.rs1use alloc::string::String;
4
5use sha2::{Digest, Sha256};
6
7use crate::{
8 decode_kv_v1, encode_kv_v1, validate_v1, EndpointCfgV1, LaneCfgV1, LaneGraphV1, PipelineSpecV1,
9 RoleCfgV1, SingleRingV1, SpecError, StageV1,
10};
11
12pub fn canonicalize_v1(spec: &PipelineSpecV1) -> PipelineSpecV1 {
14 match spec {
15 PipelineSpecV1::LaneGraph(lg) => PipelineSpecV1::LaneGraph(canonicalize_lane_graph_v1(lg)),
16 PipelineSpecV1::SingleRing(ring) => {
17 PipelineSpecV1::SingleRing(canonicalize_single_ring_v1(ring))
18 }
19 }
20}
21
22pub fn canonicalize_spec_kv_v1(input: &str) -> Result<String, SpecError> {
24 let spec = decode_kv_v1(input)?;
25 validate_v1(&spec)?;
26 Ok(encode_kv_v1(&spec))
27}
28
29pub fn canonical_encode_kv_v1(spec: &PipelineSpecV1) -> Result<String, SpecError> {
31 validate_v1(spec)?;
32 Ok(encode_kv_v1(spec))
33}
34
35pub fn canonical_spec_sha256_hex_v1(spec: &PipelineSpecV1) -> Result<String, SpecError> {
37 let canonical = canonical_encode_kv_v1(spec)?;
38 Ok(sha256_hex(canonical.as_bytes()))
39}
40
41fn canonicalize_lane_graph_v1(lg: &LaneGraphV1) -> LaneGraphV1 {
42 let mut endpoints: alloc::vec::Vec<EndpointCfgV1> = lg.endpoints.clone();
43 endpoints.sort_by(|a, b| a.name.cmp(&b.name));
44
45 let mut lanes: alloc::vec::Vec<LaneCfgV1> = lg.lanes.clone();
46 lanes.sort_by(|a, b| a.name.cmp(&b.name));
47
48 let mut roles: alloc::vec::Vec<RoleCfgV1> = lg.roles.clone();
49 roles.sort_by(|a, b| a.name().cmp(b.name()));
50
51 LaneGraphV1 {
52 endpoints,
53 lanes,
54 roles,
55 on_full: lg.on_full,
56 }
57}
58
59fn canonicalize_single_ring_v1(ring: &SingleRingV1) -> SingleRingV1 {
60 let mut stages: alloc::vec::Vec<StageV1> = ring.stages.clone();
61 for stage in &mut stages {
62 stage.depends_on.sort_unstable();
63 stage.depends_on.dedup();
64 }
65
66 SingleRingV1 {
67 shards: ring.shards,
68 ordering: ring.ordering,
69 producer: ring.producer,
70 scheduling: ring.scheduling,
71 shard_key: ring.shard_key,
72 stages,
73 }
74}
75
76fn sha256_hex(bytes: &[u8]) -> String {
77 let mut hasher = Sha256::new();
78 hasher.update(bytes);
79 let digest = hasher.finalize();
80 let mut out = String::with_capacity(digest.len() * 2);
81 for byte in digest {
82 push_hex_byte(&mut out, byte);
83 }
84 out
85}
86
87fn push_hex_byte(out: &mut String, byte: u8) {
88 const HEX: &[u8; 16] = b"0123456789abcdef";
89 out.push(HEX[(byte >> 4) as usize] as char);
90 out.push(HEX[(byte & 0x0f) as usize] as char);
91}