haematite-cli 0.21.8

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
use haematite::db::{ObservedChange, ReadOnlyDatabase};
use serde::Serialize;

use super::super::{Captured, Collection, Failure, edges, identifier, reads, relations, vectors};

#[derive(Serialize)]
pub(in crate::serve) struct DiffLine {
    collection: String,
    document_id: Option<String>,
    what: String,
    base: String,
    here: String,
}

pub(super) enum Identity {
    Collection(String),
    Document(String, String),
    Edge(String, String, String, String),
    Relation(String),
    Vector(String, String, String),
    Field(String, String),
    EqualityField(String, String),
}

pub(super) fn line(
    reader: &ReadOnlyDatabase,
    parent: &Captured,
    branch: &Captured,
    change: ObservedChange,
) -> Result<Option<DiffLine>, Failure> {
    let (key, old, new) = match change {
        ObservedChange::Added { key, value } => (key, None, Some(value)),
        ObservedChange::Removed { key, value } => (key, Some(value), None),
        ObservedChange::Modified {
            key,
            old_value,
            new_value,
        } => (key, Some(old_value), Some(new_value)),
    };
    if old == new {
        return Ok(None);
    }
    let Some(identity) = identity(&key)? else {
        return Ok(None);
    };
    let base = identity.value(reader, parent, old.as_deref())?;
    let here = identity.value(reader, branch, new.as_deref())?;
    let (collection, document_id, what) = identity.description();
    Ok(Some(DiffLine {
        collection,
        document_id,
        what,
        base,
        here,
    }))
}

pub(super) fn identity(key: &[u8]) -> Result<Option<Identity>, Failure> {
    if [b"r\0", b"b\0", b"m\0", b"i\0"]
        .iter()
        .any(|prefix| key.starts_with(*prefix))
    {
        return Ok(None);
    }
    if key.len() > 6 * 513 {
        return Err(Failure::new(
            "corrupt_key",
            "review key exceeds its component bounds",
        ));
    }
    let text = std::str::from_utf8(key).map_err(Failure::storage)?;
    let parts: Vec<_> = text.split('\0').collect();
    for part in parts.iter().skip(1) {
        identifier(part)?;
    }
    let result = match parts.as_slice() {
        ["c", collection] => Identity::Collection((*collection).to_owned()),
        ["d", collection, id] => Identity::Document((*collection).to_owned(), (*id).to_owned()),
        ["e", collection, id, relation, _, _] => Identity::Edge(
            text.to_owned(),
            (*collection).to_owned(),
            (*id).to_owned(),
            (*relation).to_owned(),
        ),
        ["n", relation] => Identity::Relation((*relation).to_owned()),
        ["v", collection, field, id] => Identity::Vector(
            (*collection).to_owned(),
            (*field).to_owned(),
            (*id).to_owned(),
        ),
        ["w", collection, field] => Identity::Field((*collection).to_owned(), (*field).to_owned()),
        ["f", "eq", collection, field] => {
            Identity::EqualityField((*collection).to_owned(), (*field).to_owned())
        }
        _ => {
            return Err(Failure::new(
                "corrupt_key",
                "review encountered an unknown owner key",
            ));
        }
    };
    Ok(Some(result))
}

pub(super) fn is_owner(key: &[u8]) -> Result<bool, Failure> {
    Ok(identity(key)?.is_some())
}

impl Identity {
    pub(super) fn description(&self) -> (String, Option<String>, String) {
        match self {
            Self::Collection(name) => (name.clone(), None, "collection".to_owned()),
            Self::Document(name, id) => (name.clone(), Some(id.clone()), "document".to_owned()),
            Self::Edge(_, name, id, relation) => {
                (name.clone(), Some(id.clone()), format!("edge {relation}"))
            }
            Self::Relation(name) => (name.clone(), None, "relation".to_owned()),
            Self::Vector(name, field, id) => {
                (name.clone(), Some(id.clone()), format!("vector {field}"))
            }
            Self::Field(name, field) => (name.clone(), None, format!("vector field {field}")),
            Self::EqualityField(name, field) => {
                (name.clone(), None, format!("equality index {field}"))
            }
        }
    }

    pub(super) fn value(
        &self,
        reader: &ReadOnlyDatabase,
        at: &Captured,
        bytes: Option<&[u8]>,
    ) -> Result<String, Failure> {
        let Some(bytes) = bytes else {
            return Ok(String::new());
        };
        match self {
            Self::Collection(name) => {
                let collection: Collection =
                    serde_json::from_slice(bytes).map_err(Failure::storage)?;
                if collection.name != *name {
                    return Err(Failure::new(
                        "collection_corrupt",
                        "collection name differs from its key",
                    ));
                }
                serde_json::to_string(&collection).map_err(Failure::storage)
            }
            Self::Document(name, id) => {
                Ok(reads::render_document(reader, at, name, id.clone(), bytes)?.raw_json)
            }
            Self::Edge(key, _, _, _) => {
                serde_json::to_string(&edges::decode_edge(b"", key, bytes)?)
                    .map_err(Failure::storage)
            }
            Self::Relation(name) => {
                serde_json::to_string(&relations::decode(name, bytes)?).map_err(Failure::storage)
            }
            Self::Vector(name, field, _) => {
                let dimension = vectors::dimension(reader, at, name, field)?.ok_or_else(|| {
                    Failure::new("vector_dimension_missing", "stored vector has no dimension")
                })?;
                serde_json::to_string(&vectors::decode(bytes, dimension)?).map_err(Failure::storage)
            }
            Self::EqualityField(name, field) => {
                super::super::field_index::render(name, field, bytes)
            }
            Self::Field(_, _) => match super::super::embedded::read_field(bytes)? {
                super::super::embedded::Field::Supplied { dimensions } => {
                    Ok(dimensions.to_string())
                }
                super::super::embedded::Field::Embedded(declaration) => {
                    serde_json::to_string(&declaration).map_err(Failure::storage)
                }
            },
        }
    }
}