use std::collections::HashMap;
use crate::error::{Error, Result};
pub trait FromRow: Sized {
fn from_row(row: &libsql::Row) -> Result<Self>;
}
pub struct ColumnMap {
map: HashMap<String, i32>,
}
impl ColumnMap {
pub fn from_row(row: &libsql::Row) -> Self {
let count = row.column_count();
let mut map = HashMap::with_capacity(count as usize);
for i in 0..count {
if let Some(name) = row.column_name(i) {
map.insert(name.to_string(), i);
}
}
Self { map }
}
pub fn index(&self, name: &str) -> Result<i32> {
self.map
.get(name)
.copied()
.ok_or_else(|| Error::internal(format!("column not found: {name}")))
}
pub fn get<T: FromValue>(&self, row: &libsql::Row, name: &str) -> Result<T> {
let idx = self.index(name)?;
let val = row.get_value(idx).map_err(Error::from)?;
T::from_value(val)
}
}
pub trait FromValue: Sized {
fn from_value(val: libsql::Value) -> Result<Self>;
}
impl FromValue for libsql::Value {
fn from_value(val: libsql::Value) -> Result<Self> {
Ok(val)
}
}
impl FromValue for String {
fn from_value(val: libsql::Value) -> Result<Self> {
match val {
libsql::Value::Text(s) => Ok(s),
libsql::Value::Null => Err(Error::internal("unexpected null value")),
_ => Err(Error::internal("invalid column type: expected text")),
}
}
}
impl FromValue for i32 {
fn from_value(val: libsql::Value) -> Result<Self> {
match val {
libsql::Value::Integer(i) => {
i32::try_from(i).map_err(|_| Error::internal("integer out of i32 range"))
}
libsql::Value::Null => Err(Error::internal("unexpected null value")),
_ => Err(Error::internal("invalid column type: expected integer")),
}
}
}
impl FromValue for u32 {
fn from_value(val: libsql::Value) -> Result<Self> {
match val {
libsql::Value::Integer(i) => {
u32::try_from(i).map_err(|_| Error::internal("integer out of u32 range"))
}
libsql::Value::Null => Err(Error::internal("unexpected null value")),
_ => Err(Error::internal("invalid column type: expected integer")),
}
}
}
impl FromValue for i64 {
fn from_value(val: libsql::Value) -> Result<Self> {
match val {
libsql::Value::Integer(i) => Ok(i),
libsql::Value::Null => Err(Error::internal("unexpected null value")),
_ => Err(Error::internal("invalid column type: expected integer")),
}
}
}
impl FromValue for u64 {
fn from_value(val: libsql::Value) -> Result<Self> {
match val {
libsql::Value::Integer(i) => {
u64::try_from(i).map_err(|_| Error::internal("integer out of u64 range"))
}
libsql::Value::Null => Err(Error::internal("unexpected null value")),
_ => Err(Error::internal("invalid column type: expected integer")),
}
}
}
impl FromValue for f64 {
fn from_value(val: libsql::Value) -> Result<Self> {
match val {
libsql::Value::Real(f) => Ok(f),
libsql::Value::Integer(i) => Ok(i as f64),
libsql::Value::Null => Err(Error::internal("unexpected null value")),
_ => Err(Error::internal("invalid column type: expected real")),
}
}
}
impl FromValue for bool {
fn from_value(val: libsql::Value) -> Result<Self> {
match val {
libsql::Value::Integer(0) => Ok(false),
libsql::Value::Integer(_) => Ok(true),
libsql::Value::Null => Err(Error::internal("unexpected null value")),
_ => Err(Error::internal("invalid column type: expected integer")),
}
}
}
impl FromValue for Vec<u8> {
fn from_value(val: libsql::Value) -> Result<Self> {
match val {
libsql::Value::Blob(b) => Ok(b),
libsql::Value::Null => Err(Error::internal("unexpected null value")),
_ => Err(Error::internal("invalid column type: expected blob")),
}
}
}
impl<T: FromValue> FromValue for Option<T> {
fn from_value(val: libsql::Value) -> Result<Self> {
match val {
libsql::Value::Null => Ok(None),
other => T::from_value(other).map(Some),
}
}
}