use sqlx_core::decode::Decode;
use sqlx_core::encode::{Encode, IsNull};
use sqlx_core::error::BoxDynError;
use sqlx_core::types::Type;
use spg_embedded::Value as EngineValue;
use crate::arguments::SpgArgumentValue;
use crate::database::Spg;
use crate::type_info::{Kind, SpgTypeInfo};
use crate::value::SpgValueRef;
pub(crate) fn try_vector_as_string(value: &EngineValue) -> Option<String> {
let v = match value {
EngineValue::Vector(v) => v.clone(),
EngineValue::Sq8Vector(q) => spg_storage::quantize::dequantize(q),
EngineValue::HalfVector(h) => h.to_f32_vec(),
_ => return None,
};
Some(format_vector(&v))
}
pub(crate) fn try_tsvector_as_string(value: &EngineValue) -> Option<String> {
match value {
EngineValue::TsVector(lex) => Some(format_tsvector(lex)),
_ => None,
}
}
fn format_vector(v: &[f32]) -> String {
let mut out = String::with_capacity(v.len() * 6);
out.push('[');
for (i, x) in v.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
format_f32_into(&mut out, *x);
}
out.push(']');
out
}
fn format_f32_into(out: &mut String, x: f32) {
use core::fmt::Write;
let _ = write!(out, "{x}");
}
fn format_tsvector(lexs: &[spg_storage::TsLexeme]) -> String {
let mut out = String::with_capacity(lexs.len() * 12);
for (i, l) in lexs.iter().enumerate() {
if i > 0 {
out.push(' ');
}
out.push('\'');
for c in l.word.chars() {
if c == '\'' {
out.push('\'');
}
out.push(c);
}
out.push('\'');
if !l.positions.is_empty() {
for (pi, p) in l.positions.iter().enumerate() {
out.push(if pi == 0 { ':' } else { ',' });
let _ = core::fmt::Write::write_fmt(&mut out, format_args!("{p}"));
}
match l.weight {
3 => out.push('A'),
2 => out.push('B'),
1 => out.push('C'),
_ => {}
}
}
}
out
}
impl Type<Spg> for Vec<f32> {
fn type_info() -> SpgTypeInfo {
SpgTypeInfo::of(Kind::Vector)
}
fn compatible(ty: &SpgTypeInfo) -> bool {
matches!(ty.kind(), Kind::Vector)
}
}
impl<'q> Encode<'q, Spg> for Vec<f32> {
fn encode_by_ref(&self, buf: &mut Vec<SpgArgumentValue<'q>>) -> Result<IsNull, BoxDynError> {
buf.push(SpgArgumentValue {
value: EngineValue::Vector(self.clone()),
type_info: Some(<Vec<f32> as Type<Spg>>::type_info()),
_phantom: core::marker::PhantomData,
});
Ok(IsNull::No)
}
}
impl<'r> Decode<'r, Spg> for Vec<f32> {
fn decode(value: SpgValueRef<'r>) -> Result<Self, BoxDynError> {
match value.engine() {
EngineValue::Vector(v) => Ok(v.clone()),
EngineValue::Sq8Vector(q) => Ok(spg_storage::quantize::dequantize(q)),
EngineValue::HalfVector(h) => Ok(h.to_f32_vec()),
other => Err(format!("cannot decode {other:?} as Vec<f32> / VECTOR").into()),
}
}
}