use serde_json::Value as JsonValue;
use sha2::{Digest, Sha256};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PayloadDigest {
pub sha256: String,
pub byte_len: usize,
}
#[must_use]
pub fn payload_digest(body: &JsonValue) -> PayloadDigest {
let canonical = canonical_json(body);
PayloadDigest {
sha256: hex::encode(Sha256::digest(canonical.as_bytes())),
byte_len: canonical.len(),
}
}
fn canonical_json(value: &JsonValue) -> String {
fn sort(value: &JsonValue) -> JsonValue {
match value {
JsonValue::Object(map) => {
let mut entries: Vec<(&String, &JsonValue)> = map.iter().collect();
entries.sort_by(|a, b| a.0.cmp(b.0));
let mut out = serde_json::Map::with_capacity(entries.len());
for (key, item) in entries {
out.insert(key.clone(), sort(item));
}
JsonValue::Object(out)
},
JsonValue::Array(items) => JsonValue::Array(items.iter().map(sort).collect()),
other => other.clone(),
}
}
sort(value).to_string()
}