use crate::Result;
use serde::Serialize;
use sha2::{Digest, Sha256};
pub fn canonicalize<T: Serialize>(value: &T) -> Result<Vec<u8>> {
serde_json_canonicalizer::to_vec(value)
.map_err(|e| crate::Error::Validation(format!("JCS canonicalization failed: {}", e)))
}
pub fn hash<T: Serialize>(value: &T) -> Result<[u8; 32]> {
let canonical = canonicalize(value)?;
let mut hasher = Sha256::new();
hasher.update(&canonical);
Ok(hasher.finalize().into())
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_canonical_order() {
let value = json!({"b": 2, "a": 1});
let canonical = canonicalize(&value).unwrap();
assert_eq!(canonical, b"{\"a\":1,\"b\":2}");
}
#[test]
fn test_nested_canonical() {
let value = json!({"z": {"b": 2, "a": 1}, "a": []});
let canonical = canonicalize(&value).unwrap();
assert_eq!(canonical, b"{\"a\":[],\"z\":{\"a\":1,\"b\":2}}");
}
#[test]
fn test_hash_deterministic() {
let value = json!({"hello": "world"});
let hash1 = hash(&value).unwrap();
let hash2 = hash(&value).unwrap();
assert_eq!(hash1, hash2);
}
#[test]
fn test_unicode_handling() {
let value = json!({"emoji": "🎉", "text": "héllo"});
let canonical = canonicalize(&value).unwrap();
let canonical_str = String::from_utf8(canonical).unwrap();
assert!(canonical_str.contains("emoji"));
assert!(canonical_str.contains("text"));
}
#[test]
fn test_number_formatting() {
let value = json!({"int": 42, "float": 3.125});
let canonical = canonicalize(&value).unwrap();
let canonical_str = String::from_utf8(canonical).unwrap();
assert!(canonical_str.contains("42"));
assert!(canonical_str.contains("3.125"));
}
#[test]
fn test_special_characters() {
let value = json!({"quote": "he said \"hello\"", "newline": "line1\nline2"});
let canonical = canonicalize(&value).unwrap();
let canonical_str = String::from_utf8(canonical).unwrap();
assert!(canonical_str.contains(r#"\""#));
assert!(canonical_str.contains(r"\n"));
}
}