use velesdb_core::DistanceMetric as CoreDistanceMetric;
use velesdb_core::FusionStrategy as CoreFusionStrategy;
#[derive(Debug, thiserror::Error, uniffi::Error)]
pub enum VelesError {
#[error("[{code}] Database error: {message}")]
Database {
message: String,
code: String,
recoverable: bool,
},
#[error("Collection error: {message}")]
Collection { message: String },
#[error("Dimension mismatch: expected {expected}, got {actual}")]
DimensionMismatch { expected: u32, actual: u32 },
}
impl VelesError {
#[must_use]
pub fn database(message: String) -> Self {
VelesError::Database {
message,
code: String::new(),
recoverable: true,
}
}
}
impl From<velesdb_core::Error> for VelesError {
fn from(err: velesdb_core::Error) -> Self {
let code = err.code().to_string();
let recoverable = err.is_recoverable();
match err {
velesdb_core::Error::DimensionMismatch { expected, actual } =>
{
#[allow(clippy::cast_possible_truncation)]
VelesError::DimensionMismatch {
expected: expected as u32,
actual: actual as u32,
}
}
velesdb_core::Error::CollectionNotFound(name) => VelesError::Collection {
message: format!("Collection not found: {name}"),
},
velesdb_core::Error::CollectionExists(name) => VelesError::Collection {
message: format!("Collection already exists: {name}"),
},
other => VelesError::Database {
message: other.to_string(),
code,
recoverable,
},
}
}
}
#[derive(Debug, Clone, Copy, uniffi::Enum)]
pub enum DistanceMetric {
Cosine,
Euclidean,
DotProduct,
Hamming,
Jaccard,
}
impl From<DistanceMetric> for CoreDistanceMetric {
fn from(metric: DistanceMetric) -> Self {
match metric {
DistanceMetric::Cosine => CoreDistanceMetric::Cosine,
DistanceMetric::Euclidean => CoreDistanceMetric::Euclidean,
DistanceMetric::DotProduct => CoreDistanceMetric::DotProduct,
DistanceMetric::Hamming => CoreDistanceMetric::Hamming,
DistanceMetric::Jaccard => CoreDistanceMetric::Jaccard,
}
}
}
#[derive(Debug, Clone, Copy, uniffi::Enum)]
pub enum StorageMode {
Full,
Sq8,
Binary,
ProductQuantization,
Rabitq,
}
impl From<StorageMode> for velesdb_core::StorageMode {
fn from(mode: StorageMode) -> Self {
match mode {
StorageMode::Full => velesdb_core::StorageMode::Full,
StorageMode::Sq8 => velesdb_core::StorageMode::SQ8,
StorageMode::Binary => velesdb_core::StorageMode::Binary,
StorageMode::ProductQuantization => velesdb_core::StorageMode::ProductQuantization,
StorageMode::Rabitq => velesdb_core::StorageMode::RaBitQ,
}
}
}
#[derive(Debug, Clone, Default, uniffi::Enum)]
pub enum SearchQuality {
Fast,
#[default]
Balanced,
Accurate,
Perfect,
Custom {
ef: u32,
},
Adaptive {
min_ef: u32,
max_ef: u32,
},
AutoTune,
}
impl From<SearchQuality> for velesdb_core::SearchQuality {
fn from(quality: SearchQuality) -> Self {
match quality {
SearchQuality::Fast => velesdb_core::SearchQuality::Fast,
SearchQuality::Balanced => velesdb_core::SearchQuality::Balanced,
SearchQuality::Accurate => velesdb_core::SearchQuality::Accurate,
SearchQuality::Perfect => velesdb_core::SearchQuality::Perfect,
SearchQuality::Custom { ef } => {
velesdb_core::SearchQuality::Custom(usize::try_from(ef).unwrap_or(usize::MAX))
}
SearchQuality::Adaptive { min_ef, max_ef } => velesdb_core::SearchQuality::Adaptive {
min_ef: usize::try_from(min_ef).unwrap_or(usize::MAX),
max_ef: usize::try_from(max_ef).unwrap_or(usize::MAX),
},
SearchQuality::AutoTune => velesdb_core::SearchQuality::AutoTune,
}
}
}
#[derive(Debug, Clone, uniffi::Enum)]
pub enum FusionStrategy {
Average,
Maximum,
Rrf {
k: u32,
},
Weighted {
avg_weight: f32,
max_weight: f32,
hit_weight: f32,
},
RelativeScore {
dense_weight: f32,
sparse_weight: f32,
},
}
impl From<FusionStrategy> for CoreFusionStrategy {
fn from(strategy: FusionStrategy) -> Self {
match strategy {
FusionStrategy::Average => CoreFusionStrategy::Average,
FusionStrategy::Maximum => CoreFusionStrategy::Maximum,
FusionStrategy::Rrf { k } => CoreFusionStrategy::RRF { k },
FusionStrategy::Weighted {
avg_weight,
max_weight,
hit_weight,
} => CoreFusionStrategy::Weighted {
avg_weight,
max_weight,
hit_weight,
},
FusionStrategy::RelativeScore {
dense_weight,
sparse_weight,
} => CoreFusionStrategy::RelativeScore {
dense_weight,
sparse_weight,
},
}
}
}
impl Default for FusionStrategy {
fn default() -> Self {
Self::Rrf { k: 60 }
}
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct VelesSparseVector {
pub indices: Vec<u32>,
pub values: Vec<f32>,
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct PqTrainConfig {
pub m: u32,
pub k: u32,
pub opq: bool,
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct SearchResult {
pub id: u64,
pub score: f32,
pub payload: Option<String>,
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct VelesPoint {
pub id: u64,
pub vector: Vec<f32>,
pub payload: Option<String>,
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct IndividualSearchRequest {
pub vector: Vec<f32>,
pub top_k: u32,
pub filter: Option<String>,
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct MobileCollectionStats {
pub total_points: u64,
pub payload_size_bytes: u64,
pub row_count: u64,
pub deleted_count: u64,
pub avg_row_size_bytes: u64,
pub total_size_bytes: u64,
pub field_stats_count: u32,
pub column_stats_count: u32,
pub index_stats_count: u32,
}
impl From<velesdb_core::collection::stats::CollectionStats> for MobileCollectionStats {
fn from(stats: velesdb_core::collection::stats::CollectionStats) -> Self {
Self {
total_points: stats.total_points,
payload_size_bytes: stats.payload_size_bytes,
row_count: stats.row_count,
deleted_count: stats.deleted_count,
avg_row_size_bytes: stats.avg_row_size_bytes,
total_size_bytes: stats.total_size_bytes,
field_stats_count: u32::try_from(stats.field_stats.len()).unwrap_or(u32::MAX),
column_stats_count: u32::try_from(stats.column_stats.len()).unwrap_or(u32::MAX),
index_stats_count: u32::try_from(stats.index_stats.len()).unwrap_or(u32::MAX),
}
}
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct MobileCollectionDiagnostics {
pub has_vectors: bool,
pub search_ready: bool,
pub dimension_configured: bool,
pub point_count: u64,
pub index_health: String,
pub index_health_detail: Option<String>,
}
impl From<velesdb_core::collection::CollectionDiagnostics> for MobileCollectionDiagnostics {
fn from(diag: velesdb_core::collection::CollectionDiagnostics) -> Self {
use velesdb_core::collection::IndexHealth;
let (index_health, index_health_detail) = match diag.index_health {
IndexHealth::Healthy => ("healthy".to_string(), None),
IndexHealth::Empty => ("empty".to_string(), None),
IndexHealth::NeedsRebuild(reason) => ("needs_rebuild".to_string(), Some(reason)),
_ => ("unknown".to_string(), None),
};
Self {
has_vectors: diag.has_vectors,
search_ready: diag.search_ready,
dimension_configured: diag.dimension_configured,
point_count: u64::try_from(diag.point_count).unwrap_or(u64::MAX),
index_health,
index_health_detail,
}
}
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct MobileIndexInfo {
pub label: String,
pub property: String,
pub index_type: String,
pub cardinality: u64,
pub memory_bytes: u64,
}
impl From<velesdb_core::IndexInfo> for MobileIndexInfo {
fn from(value: velesdb_core::IndexInfo) -> Self {
Self {
label: value.label,
property: value.property,
index_type: value.index_type,
cardinality: u64::try_from(value.cardinality).unwrap_or(u64::MAX),
memory_bytes: u64::try_from(value.memory_bytes).unwrap_or(u64::MAX),
}
}
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct MobileQueryLimits {
pub max_depth: u32,
pub max_cardinality: u64,
pub memory_limit_bytes: u64,
pub timeout_ms: u64,
pub rate_limit_qps: u32,
pub circuit_failure_threshold: u32,
pub circuit_recovery_seconds: u64,
}
impl From<velesdb_core::guardrails::QueryLimits> for MobileQueryLimits {
fn from(v: velesdb_core::guardrails::QueryLimits) -> Self {
Self {
max_depth: v.max_depth,
max_cardinality: u64::try_from(v.max_cardinality).unwrap_or(u64::MAX),
memory_limit_bytes: u64::try_from(v.memory_limit_bytes).unwrap_or(u64::MAX),
timeout_ms: v.timeout_ms,
rate_limit_qps: v.rate_limit_qps,
circuit_failure_threshold: v.circuit_failure_threshold,
circuit_recovery_seconds: v.circuit_recovery_seconds,
}
}
}
impl From<MobileQueryLimits> for velesdb_core::guardrails::QueryLimits {
fn from(v: MobileQueryLimits) -> Self {
Self {
max_depth: v.max_depth,
max_cardinality: usize::try_from(v.max_cardinality).unwrap_or(usize::MAX),
memory_limit_bytes: usize::try_from(v.memory_limit_bytes).unwrap_or(usize::MAX),
timeout_ms: v.timeout_ms,
rate_limit_qps: v.rate_limit_qps,
circuit_failure_threshold: v.circuit_failure_threshold,
circuit_recovery_seconds: v.circuit_recovery_seconds,
}
}
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct MobileDeferredIndexerConfig {
pub enabled: bool,
pub merge_threshold: u64,
pub max_buffer_age_ms: u64,
}
impl From<MobileDeferredIndexerConfig>
for velesdb_core::collection::streaming::DeferredIndexerConfig
{
fn from(v: MobileDeferredIndexerConfig) -> Self {
Self {
enabled: v.enabled,
merge_threshold: usize::try_from(v.merge_threshold).unwrap_or(usize::MAX),
max_buffer_age_ms: v.max_buffer_age_ms,
}
}
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct MobileAsyncIndexBuilderConfig {
pub merge_threshold: u64,
pub segment_count: Option<u32>,
}
impl From<MobileAsyncIndexBuilderConfig>
for velesdb_core::collection::streaming::AsyncIndexBuilderConfig
{
fn from(v: MobileAsyncIndexBuilderConfig) -> Self {
Self {
merge_threshold: usize::try_from(v.merge_threshold).unwrap_or(usize::MAX),
segment_count: v.segment_count.map(|s| s as usize),
}
}
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct MobileStreamingConfig {
pub buffer_size: u64,
pub batch_size: u64,
pub flush_interval_ms: u64,
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct MobileAdvancedConfig {
pub pq_rescore_oversampling: Option<u32>,
pub deferred_indexing: Option<MobileDeferredIndexerConfig>,
pub async_index_builder: Option<MobileAsyncIndexBuilderConfig>,
}
#[cfg(test)]
#[path = "types_tests.rs"]
mod error_tests;