kapsl-rag 0.1.0

Retrieval-augmented generation implementation with vector store and WASM plugins for Kapsl
Documentation
use std::fs;
use std::io;
use std::path::PathBuf;

#[derive(thiserror::Error, Debug)]
pub enum DocStoreError {
    #[error("io error: {0}")]
    Io(String),
}

impl From<io::Error> for DocStoreError {
    fn from(err: io::Error) -> Self {
        DocStoreError::Io(err.to_string())
    }
}

#[derive(Debug, Clone)]
pub struct DocKey {
    pub tenant_id: String,
    pub workspace_id: String,
    pub source_id: String,
    pub doc_id: String,
}

pub trait DocStore: Send + Sync {
    fn put(&self, key: &DocKey, bytes: &[u8]) -> Result<PathBuf, DocStoreError>;
    fn get(&self, key: &DocKey) -> Result<Vec<u8>, DocStoreError>;
    fn delete(&self, key: &DocKey) -> Result<(), DocStoreError>;
}

#[derive(Debug, Clone)]
pub struct FsDocStore {
    pub root: PathBuf,
}

impl FsDocStore {
    pub fn new(root: impl Into<PathBuf>) -> Self {
        Self { root: root.into() }
    }

    fn path_for(&self, key: &DocKey) -> PathBuf {
        self.root
            .join(&key.tenant_id)
            .join(&key.workspace_id)
            .join(&key.source_id)
            .join(&key.doc_id)
    }
}

impl DocStore for FsDocStore {
    fn put(&self, key: &DocKey, bytes: &[u8]) -> Result<PathBuf, DocStoreError> {
        let path = self.path_for(key);
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent)?;
        }
        fs::write(&path, bytes)?;
        Ok(path)
    }

    fn get(&self, key: &DocKey) -> Result<Vec<u8>, DocStoreError> {
        let path = self.path_for(key);
        let data = fs::read(path)?;
        Ok(data)
    }

    fn delete(&self, key: &DocKey) -> Result<(), DocStoreError> {
        let path = self.path_for(key);
        if path.exists() {
            fs::remove_file(path)?;
        }
        Ok(())
    }
}