use crate::Database;
use crate::error::Result;
use crate::types::{QueryResult, Row, Value};
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use uuid::Uuid;
#[async_trait]
pub trait QueryExecutor {
type Output;
async fn execute(&self, db: &Database) -> Result<Self::Output>;
}
pub trait FromRow: Sized {
fn from_row(row: Row) -> Result<Self>;
fn from_rows(result: QueryResult) -> Result<Vec<Self>> {
result.into_iter().map(|row| Self::from_row(row)).collect()
}
fn from_single_row(result: QueryResult) -> Result<Self> {
let row = result.one()?;
Self::from_row(row)
}
fn from_optional_row(result: QueryResult) -> Result<Option<Self>> {
match result.optional()? {
Some(row) => Ok(Some(Self::from_row(row)?)),
None => Ok(None),
}
}
}
impl FromRow for bool {
fn from_row(row: Row) -> Result<Self> {
row.get_required("value")?.as_bool()
}
}
impl FromRow for i64 {
fn from_row(row: Row) -> Result<Self> {
row.get_required("value")?.as_i64()
}
}
impl FromRow for f64 {
fn from_row(row: Row) -> Result<Self> {
row.get_required("value")?.as_f64()
}
}
impl FromRow for String {
fn from_row(row: Row) -> Result<Self> {
Ok(row.get_required("value")?.as_str()?.to_string())
}
}
impl FromRow for Uuid {
fn from_row(row: Row) -> Result<Self> {
row.get_required("value")?.as_uuid()
}
}
impl FromRow for DateTime<Utc> {
fn from_row(row: Row) -> Result<Self> {
row.get_required("value")?.as_timestamp()
}
}
impl<T: FromRow> FromRow for Option<T> {
fn from_row(row: Row) -> Result<Self> {
if row.is_empty() {
return Ok(None);
}
Ok(Some(T::from_row(row)?))
}
}
pub trait FromValue: Sized {
fn from_value(value: &Value) -> Result<Self>;
}
impl FromValue for bool {
fn from_value(value: &Value) -> Result<Self> {
value.as_bool()
}
}
impl FromValue for i64 {
fn from_value(value: &Value) -> Result<Self> {
value.as_i64()
}
}
impl FromValue for f64 {
fn from_value(value: &Value) -> Result<Self> {
value.as_f64()
}
}
impl FromValue for String {
fn from_value(value: &Value) -> Result<Self> {
Ok(value.as_str()?.to_string())
}
}
impl FromValue for Uuid {
fn from_value(value: &Value) -> Result<Self> {
value.as_uuid()
}
}
impl FromValue for DateTime<Utc> {
fn from_value(value: &Value) -> Result<Self> {
value.as_timestamp()
}
}
impl<T: FromValue> FromValue for Option<T> {
fn from_value(value: &Value) -> Result<Self> {
if value.is_null() {
return Ok(None);
}
Ok(Some(T::from_value(value)?))
}
}
impl<T: FromValue> FromValue for Vec<T> {
fn from_value(value: &Value) -> Result<Self> {
let array = value.as_array()?;
array.iter().map(|v| T::from_value(v)).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_row_primitives() {
let mut row = Row::new();
row.insert("value".to_string(), Value::Bool(true));
assert_eq!(bool::from_row(row).unwrap(), true);
let mut row = Row::new();
row.insert("value".to_string(), Value::Integer(42));
assert_eq!(i64::from_row(row).unwrap(), 42);
let mut row = Row::new();
row.insert("value".to_string(), Value::Float(3.14));
assert!((f64::from_row(row).unwrap() - 3.14).abs() < 0.01);
let mut row = Row::new();
row.insert("value".to_string(), Value::String("hello".to_string()));
assert_eq!(String::from_row(row).unwrap(), "hello");
}
#[test]
fn test_from_row_uuid() {
let uuid = Uuid::new_v4();
let mut row = Row::new();
row.insert("value".to_string(), Value::Uuid(uuid));
assert_eq!(Uuid::from_row(row).unwrap(), uuid);
}
#[test]
fn test_from_row_timestamp() {
let now = Utc::now();
let mut row = Row::new();
row.insert("value".to_string(), Value::Timestamp(now));
assert_eq!(DateTime::<Utc>::from_row(row).unwrap(), now);
}
#[test]
fn test_from_row_option() {
let empty_row = Row::new();
let result: Option<String> = Option::from_row(empty_row).unwrap();
assert_eq!(result, None);
let mut row = Row::new();
row.insert("value".to_string(), Value::String("hello".to_string()));
let result: Option<String> = Option::from_row(row).unwrap();
assert_eq!(result, Some("hello".to_string()));
}
#[test]
fn test_from_rows() {
let mut row1 = Row::new();
row1.insert("value".to_string(), Value::Integer(1));
let mut row2 = Row::new();
row2.insert("value".to_string(), Value::Integer(2));
let result = QueryResult::with_rows(vec![row1, row2]);
let values = i64::from_rows(result).unwrap();
assert_eq!(values, vec![1, 2]);
}
#[test]
fn test_from_single_row() {
let mut row = Row::new();
row.insert("value".to_string(), Value::Integer(42));
let result = QueryResult::with_rows(vec![row]);
let value = i64::from_single_row(result).unwrap();
assert_eq!(value, 42);
let empty = QueryResult::new();
assert!(i64::from_single_row(empty).is_err());
let mut row1 = Row::new();
row1.insert("value".to_string(), Value::Integer(1));
let mut row2 = Row::new();
row2.insert("value".to_string(), Value::Integer(2));
let multiple = QueryResult::with_rows(vec![row1, row2]);
assert!(i64::from_single_row(multiple).is_err());
}
#[test]
fn test_from_optional_row() {
let empty = QueryResult::new();
let result: Option<i64> = i64::from_optional_row(empty).unwrap();
assert_eq!(result, None);
let mut row = Row::new();
row.insert("value".to_string(), Value::Integer(42));
let single = QueryResult::with_rows(vec![row]);
let result: Option<i64> = i64::from_optional_row(single).unwrap();
assert_eq!(result, Some(42));
}
#[test]
fn test_from_value_primitives() {
assert_eq!(bool::from_value(&Value::Bool(true)).unwrap(), true);
assert_eq!(i64::from_value(&Value::Integer(42)).unwrap(), 42);
assert_eq!(
String::from_value(&Value::String("hi".to_string())).unwrap(),
"hi"
);
}
#[test]
fn test_from_value_option() {
let null = Value::Null;
let result: Option<i64> = Option::from_value(&null).unwrap();
assert_eq!(result, None);
let value = Value::Integer(42);
let result: Option<i64> = Option::from_value(&value).unwrap();
assert_eq!(result, Some(42));
}
#[test]
fn test_from_value_vec() {
let array = Value::Array(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]);
let result: Vec<i64> = Vec::from_value(&array).unwrap();
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn test_from_value_type_mismatch() {
let value = Value::Integer(42);
assert!(bool::from_value(&value).is_err());
assert!(String::from_value(&value).is_err());
}
#[derive(Debug, PartialEq)]
struct TestUser {
id: i64,
name: String,
active: bool,
}
impl FromRow for TestUser {
fn from_row(row: Row) -> Result<Self> {
Ok(TestUser {
id: row.get_required("id")?.as_i64()?,
name: row.get_required("name")?.as_str()?.to_string(),
active: row.get_required("active")?.as_bool()?,
})
}
}
#[test]
fn test_custom_from_row() {
let mut row = Row::new();
row.insert("id".to_string(), Value::Integer(1));
row.insert("name".to_string(), Value::String("Alice".to_string()));
row.insert("active".to_string(), Value::Bool(true));
let user = TestUser::from_row(row).unwrap();
assert_eq!(
user,
TestUser {
id: 1,
name: "Alice".to_string(),
active: true,
}
);
}
#[test]
fn test_custom_from_rows() {
let mut row1 = Row::new();
row1.insert("id".to_string(), Value::Integer(1));
row1.insert("name".to_string(), Value::String("Alice".to_string()));
row1.insert("active".to_string(), Value::Bool(true));
let mut row2 = Row::new();
row2.insert("id".to_string(), Value::Integer(2));
row2.insert("name".to_string(), Value::String("Bob".to_string()));
row2.insert("active".to_string(), Value::Bool(false));
let result = QueryResult::with_rows(vec![row1, row2]);
let users = TestUser::from_rows(result).unwrap();
assert_eq!(users.len(), 2);
assert_eq!(users[0].name, "Alice");
assert_eq!(users[1].name, "Bob");
}
}