use std::fmt;
use serde::{Deserialize, Serialize};
use super::GraphVersion;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum IndexKind {
Bm25,
Semantic,
Vector,
}
impl IndexKind {
pub const ALL: [Self; 3] = [Self::Bm25, Self::Semantic, Self::Vector];
pub const fn as_str(self) -> &'static str {
match self {
Self::Bm25 => "bm25",
Self::Semantic => "semantic",
Self::Vector => "vector",
}
}
}
impl fmt::Display for IndexKind {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum IndexModality {
Text,
Image,
Layout,
Table,
}
impl IndexModality {
pub const TEXT: Self = Self::Text;
pub const fn as_str(self) -> &'static str {
match self {
Self::Text => "text",
Self::Image => "image",
Self::Layout => "layout",
Self::Table => "table",
}
}
}
impl fmt::Display for IndexModality {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum IndexState {
Fresh,
Stale,
Failed,
Paused,
}
impl IndexState {
pub const fn as_str(self) -> &'static str {
match self {
Self::Fresh => "fresh",
Self::Stale => "stale",
Self::Failed => "failed",
Self::Paused => "paused",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct IndexStatus {
pub kind: IndexKind,
pub index_version: u64,
pub indexed_graph_version: GraphVersion,
pub state: IndexState,
pub last_error: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct IndexCursor {
pub kind: IndexKind,
pub source_scope: String,
pub modality: IndexModality,
pub index_version: u64,
pub indexed_graph_version: GraphVersion,
pub state: IndexState,
pub last_error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_hash: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub backend_cursor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model_dimension: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct IndexLag {
pub kind: IndexKind,
pub lag_versions: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct IndexStalenessReason {
pub kind: IndexKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_scope: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modality: Option<IndexModality>,
pub reason: String,
pub lag_versions: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_error: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct IndexRefreshDiagnostics {
pub queue_depth: usize,
pub running_count: usize,
pub retrying_count: usize,
pub dead_letter_count: usize,
pub oldest_unfinished_age_ms: Option<u64>,
pub index_lag_by_kind: Vec<IndexLag>,
pub max_index_lag_versions: u64,
pub stale_index_count: usize,
pub stale_reasons: Vec<IndexStalenessReason>,
}
impl IndexStatus {
pub const fn empty(kind: IndexKind) -> Self {
Self {
kind,
index_version: 0,
indexed_graph_version: GraphVersion::ZERO,
state: IndexState::Stale,
last_error: None,
}
}
pub fn is_stale_for(&self, graph_version: GraphVersion) -> bool {
self.state != IndexState::Fresh || self.indexed_graph_version < graph_version
}
}
#[cfg(test)]
#[path = "index_tests.rs"]
mod tests;