use serde::de::value::{MapDeserializer, SeqDeserializer};
use serde::de::{DeserializeOwned, Deserializer, IntoDeserializer, Visitor};
use serde::forward_to_deserialize_any;
use turso::{Row, Value};
#[derive(thiserror::Error, Debug)]
pub enum RowError {
#[error("column `{column}`: {source}")]
Column {
column: String,
source: turso::Error,
},
#[error("{0}")]
Message(String),
}
impl serde::de::Error for RowError {
fn custom<T: core::fmt::Display>(msg: T) -> Self {
RowError::Message(msg.to_string())
}
}
pub fn from_row_with_columns<I: DeserializeOwned>(
row: &Row,
columns: &[String],
) -> Result<I, RowError> {
let mut entries = Vec::with_capacity(columns.len());
for (index, column) in columns.iter().enumerate() {
let value = row.get_value(index).map_err(|source| RowError::Column {
column: column.clone(),
source,
})?;
entries.push((column.as_str(), ValueDeserializer(value)));
}
I::deserialize(RowDeserializer { entries })
}
struct RowDeserializer<'a> {
entries: Vec<(&'a str, ValueDeserializer)>,
}
impl RowDeserializer<'_> {
fn into_first_value(self) -> Result<ValueDeserializer, RowError> {
self.entries
.into_iter()
.next()
.map(|(_, value)| value)
.ok_or_else(|| RowError::Message("cannot deserialize an empty row".to_string()))
}
}
impl<'de> Deserializer<'de> for RowDeserializer<'_> {
type Error = RowError;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
self.into_first_value()?.deserialize_any(visitor)
}
fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
self.into_first_value()?.deserialize_bool(visitor)
}
fn deserialize_f32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
self.into_first_value()?.deserialize_f32(visitor)
}
fn deserialize_f64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
self.into_first_value()?.deserialize_f64(visitor)
}
fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
self.into_first_value()?.deserialize_byte_buf(visitor)
}
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
self.into_first_value()?.deserialize_option(visitor)
}
fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
self.into_first_value()?.deserialize_unit(visitor)
}
fn deserialize_unit_struct<V: Visitor<'de>>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, RowError> {
self.into_first_value()?
.deserialize_unit_struct(name, visitor)
}
fn deserialize_newtype_struct<V: Visitor<'de>>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, RowError> {
visitor.visit_newtype_struct(self.into_first_value()?)
}
fn deserialize_tuple<V: Visitor<'de>>(
self,
_len: usize,
visitor: V,
) -> Result<V::Value, RowError> {
let values = self.entries.into_iter().map(|(_, value)| value);
visitor.visit_seq(SeqDeserializer::new(values))
}
fn deserialize_map<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
visitor.visit_map(MapDeserializer::new(self.entries.into_iter()))
}
fn deserialize_struct<V: Visitor<'de>>(
self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, RowError> {
self.deserialize_map(visitor)
}
fn deserialize_enum<V: Visitor<'de>>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, RowError> {
self.into_first_value()?
.deserialize_enum(name, variants, visitor)
}
forward_to_deserialize_any! {
i8 i16 i32 i64 u8 u16 u32 u64 char str string bytes
seq tuple_struct identifier ignored_any
}
}
struct ValueDeserializer(Value);
impl<'de> IntoDeserializer<'de, RowError> for ValueDeserializer {
type Deserializer = Self;
fn into_deserializer(self) -> Self {
self
}
}
impl<'de> Deserializer<'de> for ValueDeserializer {
type Error = RowError;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
match self.0 {
Value::Null => visitor.visit_none(),
Value::Integer(value) => visitor.visit_i64(value),
Value::Real(value) => visitor.visit_f64(value),
Value::Text(value) => visitor.visit_string(value),
Value::Blob(value) => visitor.visit_seq(value.into_deserializer()),
}
}
fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
match self.0 {
Value::Integer(value) => visitor.visit_bool(value != 0),
Value::Real(value) => visitor.visit_bool(value != 0.0),
_ => self.deserialize_any(visitor),
}
}
fn deserialize_f32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
match self.0 {
Value::Null => visitor.visit_f32(f32::NAN),
_ => self.deserialize_any(visitor),
}
}
fn deserialize_f64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
match self.0 {
Value::Null => visitor.visit_f64(f64::NAN),
_ => self.deserialize_any(visitor),
}
}
fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
match self.0 {
Value::Blob(value) => visitor.visit_byte_buf(value),
_ => self.deserialize_any(visitor),
}
}
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
match self.0 {
Value::Null => visitor.visit_none(),
_ => visitor.visit_some(self),
}
}
fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, RowError> {
match self.0 {
Value::Null => visitor.visit_unit(),
_ => self.deserialize_any(visitor),
}
}
fn deserialize_unit_struct<V: Visitor<'de>>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, RowError> {
match self.0 {
Value::Text(value) if value == name => visitor.visit_unit(),
_ => self.deserialize_any(visitor),
}
}
fn deserialize_newtype_struct<V: Visitor<'de>>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, RowError> {
visitor.visit_newtype_struct(self)
}
fn deserialize_enum<V: Visitor<'de>>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, RowError> {
match self.0 {
Value::Text(value) => visitor.visit_enum(value.into_deserializer()),
_ => self.deserialize_any(visitor),
}
}
forward_to_deserialize_any! {
i8 i16 i32 i64 u8 u16 u32 u64 char str string bytes
seq tuple tuple_struct map struct identifier ignored_any
}
}