ubl_codec/
lib.rs

1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3//! Canonical JSON✯Atomic encoding/decoding helpers + binary TLV codec.
4//!
5//! This crate provides two complementary codecs:
6//!
7//! - **JSON✯Atomic** (this module): Canonical JSON serialization with BLAKE3 CIDs
8//! - **Binary TLV** ([`binary`] module): Compact binary encoding for SIRP/frames
9
10use serde::{de::DeserializeOwned, Serialize};
11use std::io::Read;
12use thiserror::Error;
13
14pub mod binary;
15pub use binary::{
16    decode_frame, decode_varint_u64, encode_frame, encode_varint_u64, BinaryCodecError, Decoder,
17    Encoder, T_BYTES, T_CID32, T_PUBKEY32, T_SIG64, T_STR, T_U64,
18};
19
20/// Errors returned by the codec helpers.
21#[derive(Debug, Error)]
22pub enum AtomicCodecError {
23    /// Serialization/deserialization error.
24    #[error("serde: {0}")]
25    Serde(#[from] serde_json::Error),
26    /// Canonicalization failure.
27    #[error("canon: {0}")]
28    Canon(String),
29    /// YAML conversion failure.
30    #[error("yaml: {0}")]
31    Yaml(String),
32}
33
34/// Serializa um valor JSON para bytes canônicos JSON✯Atomic.
35///
36/// # Errors
37///
38/// - `AtomicCodecError::Serde` se a conversão para `Value` falhar
39/// - `AtomicCodecError::Canon` se a canonicalização JSON✯Atomic falhar
40pub fn to_canon_vec<T: Serialize>(v: &T) -> Result<Vec<u8>, AtomicCodecError> {
41    let val = serde_json::to_value(v)?;
42    json_atomic::canonize(&val).map_err(|e| AtomicCodecError::Canon(e.to_string()))
43}
44
45/// Desserializa de bytes canônicos para um tipo.
46///
47/// # Errors
48///
49/// - `AtomicCodecError::Serde` se o parse ou a desserialização falhar
50pub fn from_canon_slice<T: DeserializeOwned>(bytes: &[u8]) -> Result<T, AtomicCodecError> {
51    Ok(serde_json::from_slice(bytes)?)
52}
53
54/// Converte JSON em string para bytes canônicos.
55///
56/// # Errors
57///
58/// - `AtomicCodecError::Serde` se o JSON for inválido
59/// - `AtomicCodecError::Canon` se a canonicalização JSON✯Atomic falhar
60pub fn from_json_str_canon(s: &str) -> Result<Vec<u8>, AtomicCodecError> {
61    let v: serde_json::Value = serde_json::from_str(s)?;
62    json_atomic::canonize(&v).map_err(|e| AtomicCodecError::Canon(e.to_string()))
63}
64
65/// Calcula o CID hex (BLAKE3) de um valor serializável.
66///
67/// # Errors
68///
69/// - Propaga os mesmos erros de [`to_canon_vec`]
70pub fn to_cid_hex<T: Serialize>(v: &T) -> Result<String, AtomicCodecError> {
71    let b = to_canon_vec(v)?;
72    Ok(blake3::hash(&b).to_hex().to_string())
73}
74
75/// Valor e seus bytes canônicos já calculados.
76pub struct Canonical<T> {
77    value: T,
78    bytes: Vec<u8>,
79}
80impl<T> Canonical<T>
81where
82    T: Serialize + DeserializeOwned,
83{
84    /// Cria a partir de um valor serializável.
85    ///
86    /// # Errors
87    ///
88    /// - `AtomicCodecError::Serde` se a serialização falhar
89    /// - `AtomicCodecError::Canon` se a canonicalização JSON✯Atomic falhar
90    pub fn new(value: T) -> Result<Self, AtomicCodecError> {
91        let bytes = to_canon_vec(&value)?;
92        Ok(Self { value, bytes })
93    }
94    /// Lê de um reader de texto, parseia e canonicaliza.
95    ///
96    /// # Errors
97    ///
98    /// - `AtomicCodecError::Canon` se a leitura ou canonicalização falhar
99    /// - `AtomicCodecError::Serde` se o JSON for inválido ou desserialização falhar
100    pub fn from_reader<R: Read>(mut r: R) -> Result<Self, AtomicCodecError> {
101        let mut s = String::new();
102        r.read_to_string(&mut s)
103            .map_err(|e| AtomicCodecError::Canon(e.to_string()))?;
104        let v: serde_json::Value = serde_json::from_str(&s)?;
105        let bytes =
106            json_atomic::canonize(&v).map_err(|e| AtomicCodecError::Canon(e.to_string()))?;
107        Ok(Self {
108            value: serde_json::from_value(v)?,
109            bytes,
110        })
111    }
112    /// Referência ao valor.
113    pub const fn value(&self) -> &T {
114        &self.value
115    }
116    /// Bytes canônicos.
117    pub fn as_bytes(&self) -> &[u8] {
118        &self.bytes
119    }
120    /// Consome e retorna os bytes canônicos.
121    pub fn into_bytes(self) -> Vec<u8> {
122        self.bytes
123    }
124}
125
126/// Retorna true se a string JSON já está na forma canônica JSON✯Atomic.
127#[must_use]
128pub fn is_canonical(s: &str) -> bool {
129    serde_json::from_str::<serde_json::Value>(s)
130        .ok()
131        .and_then(|v| json_atomic::canonize(&v).ok())
132        .and_then(|b| String::from_utf8(b).ok())
133        .is_some_and(|canon| canon == s.trim())
134}
135
136/// Converte YAML (subset) → bytes canônicos JSON✯Atomic.
137///
138/// # Errors
139///
140/// - `AtomicCodecError::Yaml` se o YAML for inválido ou não puder ser convertido para JSON
141/// - `AtomicCodecError::Canon` se a canonicalização JSON✯Atomic falhar
142pub fn yaml_to_canon_vec(yaml: &str) -> Result<Vec<u8>, AtomicCodecError> {
143    let v: serde_json::Value = match serde_yaml::from_str::<serde_yaml::Value>(yaml) {
144        Ok(doc) => serde_json::to_value(doc).map_err(|e| AtomicCodecError::Yaml(e.to_string()))?,
145        Err(e) => return Err(AtomicCodecError::Yaml(e.to_string())),
146    };
147    json_atomic::canonize(&v).map_err(|e| AtomicCodecError::Canon(e.to_string()))
148}