use serde_json::Value;
use sha2::{Digest, Sha256};
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum JcsError {
#[error("number is not finite (NaN or Infinity is not permitted in JSON)")]
NonFiniteNumber,
#[error("duplicate key '{0}' in JSON object")]
DuplicateKey(String),
#[error("serialization failed: {0}")]
Serde(#[from] serde_json::Error),
}
pub fn canonicalize(value: &Value) -> Result<Vec<u8>, JcsError> {
serde_jcs::to_vec(value).map_err(JcsError::from)
}
pub fn canonicalize_serializable<T: serde::Serialize>(value: &T) -> Result<Vec<u8>, JcsError> {
serde_jcs::to_vec(value).map_err(JcsError::from)
}
pub fn canonicalize_and_hash<T: serde::Serialize>(value: &T) -> Result<[u8; 32], JcsError> {
let bytes = canonicalize_serializable(value)?;
let digest = Sha256::digest(&bytes);
Ok(digest.into())
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn canonicalize_empty_object() {
let v: Value = json!({});
let out = canonicalize(&v).unwrap();
assert_eq!(std::str::from_utf8(&out).unwrap(), "{}");
}
#[test]
fn canonicalize_sorts_keys() {
let v: Value = json!({"b": 1, "a": 2});
let out = canonicalize(&v).unwrap();
assert_eq!(std::str::from_utf8(&out).unwrap(), r#"{"a":2,"b":1}"#);
}
}