use std::error::Error;
use std::fmt;
#[derive(Debug)]
pub enum HelixError {
Transport(String),
Serialization(String),
InvalidArgument(String),
NotFound(String),
NotImplemented(String),
}
impl fmt::Display for HelixError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HelixError::Transport(msg) => write!(f, "HelixDB transport error: {}", msg),
HelixError::Serialization(msg) => write!(f, "HelixDB serialization error: {}", msg),
HelixError::InvalidArgument(msg) => write!(f, "HelixDB invalid argument: {}", msg),
HelixError::NotFound(msg) => write!(f, "HelixDB not found: {}", msg),
HelixError::NotImplemented(msg) => write!(f, "HelixDB not implemented: {}", msg),
}
}
}
impl Error for HelixError {}
#[derive(Debug, Clone)]
pub struct Vector256 {
pub elements: Vec<f32>,
}
impl Vector256 {
pub fn new(elements: Vec<f32>) -> Self {
assert_eq!(
elements.len(),
256,
"Vector256 must have exactly 256 elements"
);
Self { elements }
}
pub fn from_mlwe_coeffs(coeffs: &[i32], q: i32) -> Self {
assert_eq!(
coeffs.len(),
256,
"M-LWE coefficient vector must have 256 elements"
);
let half_q = q / 2;
let elements: Vec<f32> = coeffs
.iter()
.map(|&c| {
let centered = if c > half_q { c - q } else { c };
centered as f32 / half_q as f32
})
.collect();
Self { elements }
}
pub fn zeros() -> Self {
Self {
elements: vec![0.0f32; 256],
}
}
}
#[derive(Debug, Clone)]
pub struct StateNodePayload {
pub vault_id: [u8; 32],
pub ciphertext: Vec<u8>,
pub timestamp: u64,
pub vector_embedding: Vector256,
pub previous_node_id: Option<String>,
pub geodesic_distance: f32,
}
#[derive(Debug, Clone)]
pub struct ScoredStateNode {
pub node_id: String,
pub distance: f32,
}
pub struct HelixDbAdapter {
endpoint: String,
}
impl HelixDbAdapter {
pub async fn new(
endpoint: &str,
_ca_cert_pem: Option<&[u8]>,
_client_cert_pem: Option<&[u8]>,
_client_key_pem: Option<&[u8]>,
) -> Result<Self, Box<dyn Error>> {
Ok(Self {
endpoint: endpoint.to_string(),
})
}
pub async fn upsert_state_node(&self, node: StateNodePayload) -> Result<String, HelixError> {
let _ = node;
Err(HelixError::NotImplemented(format!(
"upsert_state_node not yet wired to gRPC endpoint: {}",
self.endpoint
)))
}
pub async fn traverse_temporal_trajectory(
&self,
vault_id: &[u8; 32],
from_timestamp: u64,
to_timestamp: u64,
max_hops: u32,
) -> Result<Vec<StateNodePayload>, HelixError> {
let _ = (vault_id, from_timestamp, to_timestamp, max_hops);
Err(HelixError::NotImplemented(
"traverse_temporal_trajectory not yet wired to gRPC".to_string(),
))
}
pub async fn vector_proximity_search(
&self,
query_vector: &Vector256,
top_k: u32,
min_timestamp: u64,
max_timestamp: u64,
) -> Result<Vec<ScoredStateNode>, HelixError> {
if query_vector.elements.len() != 256 {
return Err(HelixError::InvalidArgument(format!(
"query_vector must have 256 elements, got {}",
query_vector.elements.len()
)));
}
let _ = (query_vector, top_k, min_timestamp, max_timestamp);
Err(HelixError::NotImplemented(
"vector_proximity_search not yet wired to gRPC".to_string(),
))
}
pub async fn prune_temporal_node(
&self,
vault_id: &[u8; 32],
before_timestamp: u64,
revocation_proof: &[u8],
) -> Result<u32, HelixError> {
let _ = (vault_id, before_timestamp, revocation_proof);
Err(HelixError::NotImplemented(
"prune_temporal_node not yet wired to gRPC".to_string(),
))
}
pub fn endpoint(&self) -> &str {
&self.endpoint
}
}
#[cfg(target_arch = "wasm32")]
pub mod wasm_stub {
use super::{StateNodePayload, Vector256};
use alloc::vec::Vec;
use core::cell::RefCell;
extern crate alloc;
thread_local! {
static PENDING_NODES: RefCell<Vec<StateNodePayload>> = RefCell::new(Vec::new());
}
pub fn queue_state_node(node: StateNodePayload) {
PENDING_NODES.with(|nodes| {
nodes.borrow_mut().push(node);
});
}
pub fn drain_pending_nodes() -> Vec<StateNodePayload> {
PENDING_NODES.with(|nodes| {
let mut borrow = nodes.borrow_mut();
core::mem::take(&mut *borrow)
})
}
pub fn pending_node_count() -> usize {
PENDING_NODES.with(|nodes| nodes.borrow().len())
}
}