///|
/// Canonicalize a value and return the lowercase hexadecimal SHA-256 digest
/// of its UTF-8 canonical representation. Hashing canonical bytes gives
/// semantically equivalent JSON documents a stable content identifier.
pub fn canonical_digest(value : JsonValue) -> Result[String, CanonicalError] {
  let serialized = match canonicalize(value) {
    Ok(serialized) => serialized
    Err(error) => return Err(error)
  }
  let digest = @sha256.Digest::new()
  for byte in @utf8.encode(serialized) {
    digest.write(byte)
  }
  Ok(digest.check_sum())
}

///|
/// Hash a JSON document after parsing and canonicalizing it.
pub fn digest_json(input : String) -> Result[String, String] {
  let value = match parse(input) {
    Ok(value) => value
    Err(error) => return Err(error.message())
  }
  match canonical_digest(value) {
    Ok(digest) => Ok(digest)
    Err(error) => Err(error.message())
  }
}

///|
/// Hash untrusted input after enforcing resource limits during parsing.
pub fn digest_json_with_limits(
  input : String,
  limits : DocumentLimits,
) -> Result[String, String] {
  let value = match parse_with_limits(input, limits) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match canonical_digest(value) {
    Ok(digest) => Ok(digest)
    Err(error) => Err(error.message())
  }
}