weavatrix-search-vector 0.3.1

Persistent, mutable, bounded vector candidate search for Rust and Weavatrix
Documentation
use super::decoder::decode_snapshot;
use super::writer::{write_snapshot, write_snapshot_stream};
use super::{FORMAT_VERSION, HEADER_LEN, Header, MappedVectorIndex, SnapshotMetadata};
use crate::error::SearchError;
use crate::hnsw::VectorIndex;
use std::io::{Cursor, Read, Seek, Write};
use std::path::Path;

impl VectorIndex {
    /// Persists normalized vectors, routing data, and every HNSW replica.
    ///
    /// The snapshot is written through a temporary sibling and flushed before
    /// replacement.
    ///
    /// # Errors
    ///
    /// Returns a typed storage, allocation, or capacity error.
    pub fn save(&self, path: impl AsRef<Path>) -> Result<(), SearchError> {
        write_snapshot(self, path.as_ref())
    }

    /// Loads a persisted index into owned memory without rebuilding HNSW.
    ///
    /// # Errors
    ///
    /// Returns a typed storage, version, integrity, allocation, or config
    /// error.
    pub fn load(path: impl AsRef<Path>) -> Result<Self, SearchError> {
        MappedVectorIndex::open(path)?.to_owned()
    }

    /// Serializes the canonical snapshot into an owned byte buffer.
    ///
    /// # Errors
    ///
    /// Returns a typed storage, allocation, or capacity error.
    pub fn to_bytes(&self) -> Result<Vec<u8>, SearchError> {
        let mut cursor = Cursor::new(Vec::new());
        write_snapshot_stream(self, &mut cursor)?;
        Ok(cursor.into_inner())
    }

    /// Restores an owned index directly from a canonical snapshot buffer.
    ///
    /// # Errors
    ///
    /// Returns a typed version, integrity, allocation, or config error.
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, SearchError> {
        decode_snapshot(bytes)
    }

    /// Writes a canonical snapshot to a seekable stream.
    ///
    /// The caller should provide an empty or truncated stream.
    ///
    /// # Errors
    ///
    /// Returns a typed stream, allocation, or capacity error.
    pub fn write_to<W: Write + Seek>(&self, writer: &mut W) -> Result<(), SearchError> {
        write_snapshot_stream(self, writer)
    }

    /// Reads a complete canonical snapshot from a stream.
    ///
    /// # Errors
    ///
    /// Returns a typed stream, version, integrity, allocation, or config
    /// error.
    pub fn read_from<R: Read>(reader: &mut R) -> Result<Self, SearchError> {
        let mut bytes = Vec::new();
        reader
            .read_to_end(&mut bytes)
            .map_err(|error| SearchError::storage("read snapshot stream", &error))?;
        Self::from_bytes(&bytes)
    }

    /// Reads snapshot metadata without decoding vectors or graphs.
    ///
    /// # Errors
    ///
    /// Returns a typed storage, version, or header error.
    pub fn read_metadata(path: impl AsRef<Path>) -> Result<SnapshotMetadata, SearchError> {
        let mut file = std::fs::File::open(path)
            .map_err(|error| SearchError::storage("open snapshot metadata", &error))?;
        let mut header = [0_u8; HEADER_LEN];
        file.read_exact(&mut header)
            .map_err(|error| SearchError::storage("read snapshot metadata", &error))?;
        SnapshotMetadata::from_bytes(&header)
    }
}

impl SnapshotMetadata {
    /// Parses the fixed snapshot header from a full or header-only buffer.
    ///
    /// # Errors
    ///
    /// Returns a typed version or structural error.
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, SearchError> {
        let header = Header::parse(bytes)?;
        Ok(Self {
            format_version: FORMAT_VERSION,
            vector_count: header.count,
            serialized_bytes: header.file_len,
            config: header.config,
        })
    }
}