use crate::{error::Error, state::AppState, Result};
use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
routing::{get, post},
Json, Router,
};
use ruvector_core::{types::DbOptions, DistanceMetric, VectorDB};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Debug, Deserialize)]
pub struct CreateCollectionRequest {
pub name: String,
pub dimension: usize,
pub metric: Option<DistanceMetric>,
}
#[derive(Debug, Serialize)]
pub struct CollectionInfo {
pub name: String,
pub dimension: usize,
pub metric: DistanceMetric,
}
#[derive(Debug, Serialize)]
pub struct CollectionsList {
pub collections: Vec<String>,
}
pub fn routes() -> Router<AppState> {
Router::new()
.route("/", post(create_collection).get(list_collections))
.route("/:name", get(get_collection).delete(delete_collection))
}
async fn create_collection(
State(state): State<AppState>,
Json(req): Json<CreateCollectionRequest>,
) -> Result<impl IntoResponse> {
if state.contains_collection(&req.name) {
return Err(Error::CollectionExists(req.name));
}
let mut options = DbOptions::default();
options.dimensions = req.dimension;
options.distance_metric = req.metric.unwrap_or(DistanceMetric::Cosine);
options.storage_path = format!("memory://{}", req.name);
let db = VectorDB::new(options.clone()).map_err(Error::Core)?;
state.insert_collection(req.name.clone(), Arc::new(db));
let info = CollectionInfo {
name: req.name,
dimension: req.dimension,
metric: options.distance_metric,
};
Ok((StatusCode::CREATED, Json(info)))
}
async fn list_collections(State(state): State<AppState>) -> Result<impl IntoResponse> {
let collections = state.collection_names();
Ok(Json(CollectionsList { collections }))
}
async fn get_collection(
State(state): State<AppState>,
Path(name): Path<String>,
) -> Result<impl IntoResponse> {
let _db = state
.get_collection(&name)
.ok_or_else(|| Error::CollectionNotFound(name.clone()))?;
let info = CollectionInfo {
name,
dimension: 0, metric: DistanceMetric::Cosine, };
Ok(Json(info))
}
async fn delete_collection(
State(state): State<AppState>,
Path(name): Path<String>,
) -> Result<impl IntoResponse> {
state
.remove_collection(&name)
.ok_or_else(|| Error::CollectionNotFound(name))?;
Ok(StatusCode::NO_CONTENT)
}