kiromi-ai-memory 0.2.2

Local-first multi-tenant memory store engine: Markdown/text content on object storage, metadata in SQLite, plugin-shaped embedder/storage/metadata, hybrid text+vector search.
Documentation
// SPDX-License-Identifier: Apache-2.0 OR MIT
//! Codec plugin trait — interface stub for slice 1.
//!
//! Sits between the engine and `Storage`. Encodes / decodes blobs at-rest.
//! Slice 1 ships an implicit identity codec (the engine writes plaintext);
//! the trait + the future `Builder::codec(impl Codec)` hook exist so a
//! future encryption / compression / content-defined-chunking codec lands
//! without changing the on-disk shape or breaking SemVer.
//!
//! See spec § 12.10.

use async_trait::async_trait;
use bytes::Bytes;

use crate::error::Result;

/// Encode/decode pre-storage.
#[async_trait]
pub trait Codec: Send + Sync + std::fmt::Debug + 'static {
    /// Stable identifier (e.g. `"identity:v1"`, `"aes-gcm:v1"`).
    fn id(&self) -> &str;

    /// Encode a plaintext blob into its at-rest form.
    async fn encode(&self, plain: Bytes) -> Result<Bytes>;

    /// Decode an at-rest blob back to plaintext.
    async fn decode(&self, encoded: Bytes) -> Result<Bytes>;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[derive(Debug)]
    struct IdentityCodec;

    #[async_trait]
    impl Codec for IdentityCodec {
        fn id(&self) -> &str {
            "test:identity:v1"
        }
        async fn encode(&self, plain: Bytes) -> Result<Bytes> {
            Ok(plain)
        }
        async fn decode(&self, encoded: Bytes) -> Result<Bytes> {
            Ok(encoded)
        }
    }

    #[tokio::test]
    async fn object_safe_round_trip() {
        let boxed: Box<dyn Codec> = Box::new(IdentityCodec);
        let plain = Bytes::from_static(b"hello world");
        let enc = boxed.encode(plain.clone()).await.unwrap();
        let dec = boxed.decode(enc).await.unwrap();
        assert_eq!(dec, plain);
        assert_eq!(boxed.id(), "test:identity:v1");
    }
}