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)
}
},
}
}
}