agent-sdk-store-sqlite 0.1.0-alpha.4

SQLite-backed durable store adapters for the Agent SDK.
Documentation
use std::path::Path;

use agent_sdk_core::{
    AgentError, AgentErrorKind, ContentResolutionError, ContentResolutionErrorKind, PolicyRef,
    RetryClassification, content::ContentRef,
};
use rusqlite::Connection;
use serde::{Serialize, de::DeserializeOwned};
use sha2::{Digest, Sha256};

pub(crate) fn sqlite_error(error: rusqlite::Error) -> AgentError {
    AgentError::new(
        AgentErrorKind::RecoveryRepairNeeded,
        RetryClassification::Retryable,
        format!("sqlite store error: {error}"),
    )
}

pub(crate) fn serde_error(error: impl std::fmt::Display) -> AgentError {
    AgentError::contract_violation(format!("sqlite store JSON error: {error}"))
}

pub(crate) fn journal_error(message: impl Into<String>) -> AgentError {
    AgentError::new(
        AgentErrorKind::JournalFailure,
        RetryClassification::RepairNeeded,
        message,
    )
}

pub(crate) fn content_error(
    kind: ContentResolutionErrorKind,
    content_ref: ContentRef,
    policy_refs: Vec<PolicyRef>,
) -> ContentResolutionError {
    ContentResolutionError {
        kind,
        redacted_summary: content_ref.redacted_summary.clone(),
        content_ref: Box::new(content_ref),
        policy_refs,
    }
}

pub(crate) fn open(path: &Path) -> Result<Connection, AgentError> {
    if let Some(parent) = path.parent() {
        std::fs::create_dir_all(parent).map_err(|error| {
            AgentError::new(
                AgentErrorKind::RecoveryRepairNeeded,
                RetryClassification::Retryable,
                format!("sqlite store directory error: {error}"),
            )
        })?;
    }
    Connection::open(path).map_err(sqlite_error)
}

pub(crate) fn init(path: &Path, schema: &str) -> Result<(), AgentError> {
    let connection = open(path)?;
    connection.execute_batch(schema).map_err(sqlite_error)
}

pub(crate) fn encode<T: Serialize>(value: &T) -> Result<String, AgentError> {
    serde_json::to_string(value).map_err(serde_error)
}

pub(crate) fn decode<T: DeserializeOwned>(value: &str) -> Result<T, AgentError> {
    serde_json::from_str(value).map_err(serde_error)
}

pub(crate) fn sha256_hex(bytes: &[u8]) -> String {
    format!("{:x}", Sha256::digest(bytes))
}