use std::fmt;
pub mod hash;
pub mod json;
pub mod rust;
pub mod ttl;
#[derive(Debug, thiserror::Error)]
pub enum CanonicalError {
#[error("Format error: {0}")]
Format(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Serialization error: {0}")]
Serialization(String),
#[error("Hash error: {0}")]
Hash(String),
#[error("Invalid input: {0}")]
Invalid(String),
}
pub type Result<T> = std::result::Result<T, CanonicalError>;
pub trait Canonicalizer {
type Input;
type Output;
fn canonicalize(&self, input: Self::Input) -> Result<Self::Output>;
fn hash(&self, input: Self::Input) -> Result<String>
where
Self::Output: AsRef<[u8]>,
{
let canonical = self.canonicalize(input)?;
hash::compute_hash(&canonical)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Canonical<T> {
inner: T,
}
impl<T> Canonical<T> {
pub fn new_unchecked(value: T) -> Self {
Self { inner: value }
}
pub fn into_inner(self) -> T {
self.inner
}
pub fn as_inner(&self) -> &T {
&self.inner
}
}
impl<T: fmt::Display> fmt::Display for Canonical<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl<T: AsRef<[u8]>> AsRef<[u8]> for Canonical<T> {
fn as_ref(&self) -> &[u8] {
self.inner.as_ref()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_canonical_wrapper() {
let canonical = Canonical::new_unchecked("test");
assert_eq!(canonical.as_inner(), &"test");
assert_eq!(canonical.into_inner(), "test");
}
}