Skip to main content

appcore_dnt/
codec.rs

1// =============================================================================
2//        #######
3//     ###       ###     F: codec.rs
4//    ##   ## ##   ##    P: AppCore-Runtime
5//         ## ##
6//                       C: 2026/08/02 00:04:12 by dnettoRaw
7//    ##   ## ##   ##    U: 2026/08/02 10:29:16 by dnettoRaw
8//      ###########      S: 1.0.1-rc.8
9// =============================================================================
10
11//! DNT codec contracts.
12
13use crate::{CodecError, CodecId};
14use zeroize::Zeroize;
15
16/// Payload codec used before encryption and after authentication.
17pub trait DntCodec: Send + Sync {
18    /// Returns the stable codec identifier.
19    fn codec_id(&self) -> CodecId;
20    /// Returns true when this codec can decode the authenticated identifier.
21    fn matches_codec_id(&self, codec_id: &CodecId) -> bool {
22        self.codec_id() == *codec_id
23    }
24    /// Encodes caller-owned bytes before sealing.
25    fn encode(&self, value: &[u8]) -> Result<Vec<u8>, CodecError>;
26    /// Decodes authenticated plaintext bytes after opening.
27    fn decode(&self, payload: &[u8]) -> Result<Vec<u8>, CodecError>;
28    /// Decodes an owned authenticated payload after opening.
29    ///
30    /// Codecs that are identity transforms can return `payload` directly to
31    /// avoid an allocation in the read path. Transforming codecs may keep the
32    /// default implementation.
33    fn decode_owned(&self, mut payload: Vec<u8>) -> Result<Vec<u8>, CodecError> {
34        let decoded = self.decode(&payload);
35        payload.zeroize();
36        decoded
37    }
38}
39
40/// Identity codec for arbitrary binary payloads.
41#[derive(Debug, Clone, Copy, Default)]
42pub struct BytesCodec;
43
44impl DntCodec for BytesCodec {
45    fn codec_id(&self) -> CodecId {
46        // appcore-norm: allow(clippy::expect_used) reason: codec identifier is a validated package constant
47        CodecId::new("bytes").expect("static codec id")
48    }
49
50    fn matches_codec_id(&self, codec_id: &CodecId) -> bool {
51        codec_id.as_str() == "bytes"
52    }
53
54    fn encode(&self, value: &[u8]) -> Result<Vec<u8>, CodecError> {
55        Ok(value.to_vec())
56    }
57
58    fn decode(&self, payload: &[u8]) -> Result<Vec<u8>, CodecError> {
59        Ok(payload.to_vec())
60    }
61
62    fn decode_owned(&self, payload: Vec<u8>) -> Result<Vec<u8>, CodecError> {
63        Ok(payload)
64    }
65}
66
67/// Identity codec for caller-validated JSON bytes.
68#[derive(Debug, Clone, Copy, Default)]
69pub struct IdentityJsonCodec;
70
71impl DntCodec for IdentityJsonCodec {
72    fn codec_id(&self) -> CodecId {
73        // appcore-norm: allow(clippy::expect_used) reason: codec identifier is a validated package constant
74        CodecId::new("json").expect("static codec id")
75    }
76
77    fn matches_codec_id(&self, codec_id: &CodecId) -> bool {
78        codec_id.as_str() == "json"
79    }
80
81    fn encode(&self, value: &[u8]) -> Result<Vec<u8>, CodecError> {
82        Ok(value.to_vec())
83    }
84
85    fn decode(&self, payload: &[u8]) -> Result<Vec<u8>, CodecError> {
86        Ok(payload.to_vec())
87    }
88
89    fn decode_owned(&self, payload: Vec<u8>) -> Result<Vec<u8>, CodecError> {
90        Ok(payload)
91    }
92}