use serde::{Deserialize, Serialize};
use crate::{DistanceMetric, StorageError, StorageLocator};
use super::{ApplyDefaultLocator, HasLocator};
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct IndexInfo {
pub name: Option<String>,
pub columns: Vec<String>,
pub ready: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum IndexKind {
Scalar,
FullText,
Vector {
dimension: usize,
distance: Option<DistanceMetric>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CreateIndexRequest {
pub locator: StorageLocator,
pub columns: Vec<String>,
pub kind: IndexKind,
pub wait_timeout_ms: Option<u64>,
}
impl HasLocator for CreateIndexRequest {
fn locator_mut(&mut self) -> &mut StorageLocator {
&mut self.locator
}
}
impl CreateIndexRequest {
pub(crate) fn validate(&self) -> Result<(), StorageError> {
if self.columns.is_empty() {
return Err(StorageError::InvalidRequest(
"index creation requires at least one column".into(),
));
}
if self.columns.iter().any(|column| column.trim().is_empty()) {
return Err(StorageError::InvalidRequest(
"index columns must not be empty".into(),
));
}
if let IndexKind::Vector { dimension, .. } = &self.kind {
if self.columns.len() != 1 {
return Err(StorageError::InvalidRequest(
"vector index creation requires exactly one column".into(),
));
}
if *dimension == 0 {
return Err(StorageError::InvalidRequest(
"vector index dimension must be positive".into(),
));
}
}
if self.wait_timeout_ms == Some(0) {
return Err(StorageError::InvalidRequest(
"index wait timeout must be positive when configured".into(),
));
}
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct WaitForIndexRequest {
pub locator: StorageLocator,
pub index_names: Vec<String>,
pub timeout_ms: u64,
}
impl HasLocator for WaitForIndexRequest {
fn locator_mut(&mut self) -> &mut StorageLocator {
&mut self.locator
}
}
impl WaitForIndexRequest {
fn validate(&self) -> Result<(), StorageError> {
if self.index_names.is_empty() {
return Err(StorageError::InvalidRequest(
"wait-for-index requires at least one index name".into(),
));
}
if self.index_names.iter().any(|name| name.trim().is_empty()) {
return Err(StorageError::InvalidRequest(
"index names must not be empty".into(),
));
}
if self.timeout_ms == 0 {
return Err(StorageError::InvalidRequest(
"wait-for-index timeout must be positive".into(),
));
}
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum IndexRequest {
Create(CreateIndexRequest),
List {
locator: StorageLocator,
},
Drop {
locator: StorageLocator,
name: String,
},
WaitFor(WaitForIndexRequest),
}
impl IndexRequest {
pub(crate) fn validate(&self) -> Result<(), StorageError> {
match self {
Self::Create(request) => request.validate(),
Self::Drop { name, .. } if name.trim().is_empty() => Err(StorageError::InvalidRequest(
"index name must not be empty".into(),
)),
Self::WaitFor(request) => request.validate(),
Self::List { .. } | Self::Drop { .. } => Ok(()),
}
}
}
impl ApplyDefaultLocator for IndexRequest {
fn apply_default_locator(self, default: &StorageLocator) -> Self {
match self {
Self::Create(req) => Self::Create(req.apply_default_locator(default)),
Self::List { locator } => Self::List {
locator: locator.with_defaults_from(default),
},
Self::Drop { locator, name } => Self::Drop {
locator: locator.with_defaults_from(default),
name,
},
Self::WaitFor(req) => Self::WaitFor(req.apply_default_locator(default)),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum IndexResponse {
Completed,
Indexes(Vec<IndexInfo>),
}