storage-kit 0.0.1

Portable database and transactional state-store Kit.
Documentation
use serde_json::Value;

use crate::{PageToken, QueryResponse, ScoreKind, ScoredRecord, StorageError};

use super::super::codec;

pub(super) enum SurrealScoring {
    Unscored(ScoreKind),
    RelevanceEnvelope,
}

impl SurrealScoring {
    pub(super) fn response(
        self,
        rows: Value,
        next_page: Option<PageToken>,
    ) -> Result<QueryResponse, StorageError> {
        let Value::Array(rows) = rows else {
            return Err(StorageError::Serialization(
                "Surreal query response must be an array".to_owned(),
            ));
        };
        let hits = rows
            .into_iter()
            .map(|row| self.decode(row))
            .collect::<Result<Vec<_>, _>>()?;
        Ok(QueryResponse::from_hits(hits, next_page))
    }

    fn decode(&self, row: Value) -> Result<ScoredRecord, StorageError> {
        match self {
            Self::Unscored(kind) => Ok(ScoredRecord {
                record: codec::value_to_record(row)?,
                raw_score: None,
                score_kind: kind.clone(),
            }),
            Self::RelevanceEnvelope => {
                let Value::Object(mut envelope) = row else {
                    return Err(StorageError::Serialization(
                        "Surreal relevance response must contain a record envelope".to_owned(),
                    ));
                };
                let record = envelope.remove("record").ok_or_else(|| {
                    StorageError::Serialization(
                        "Surreal relevance response is missing its record".to_owned(),
                    )
                })?;
                let raw_score = match envelope.remove("score") {
                    None | Some(Value::Null) => None,
                    Some(Value::Number(score)) => Some(score.as_f64().ok_or_else(|| {
                        StorageError::Serialization(
                            "Surreal relevance score is not representable".to_owned(),
                        )
                    })?),
                    Some(_) => {
                        return Err(StorageError::Serialization(
                            "Surreal relevance score must be numeric or absent".to_owned(),
                        ));
                    }
                };
                Ok(ScoredRecord {
                    record: codec::value_to_record(record)?,
                    raw_score,
                    score_kind: ScoreKind::Relevance,
                })
            }
        }
    }
}

pub(super) fn next_page_token_from_rows(
    rows: &Value,
    limit: Option<usize>,
    effective_offset: Option<usize>,
) -> Result<Option<PageToken>, StorageError> {
    let count = match rows {
        Value::Array(values) => values.len(),
        other => {
            return Err(StorageError::Serialization(format!(
                "Surreal query response must be an array, got {other}"
            )));
        }
    };
    Ok(PageToken::next_offset(limit, effective_offset, count))
}