use crate::error::{QueryError, Result};
use chrono::{DateTime, Utc};
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Debug, Clone)]
pub struct QueryResult {
pub rows: Vec<Row>,
pub affected_rows: usize,
}
impl QueryResult {
pub fn new() -> Self {
Self {
rows: Vec::new(),
affected_rows: 0,
}
}
pub fn with_rows(rows: Vec<Row>) -> Self {
Self {
rows,
affected_rows: 0,
}
}
pub fn with_affected_rows(affected_rows: usize) -> Self {
Self {
rows: Vec::new(),
affected_rows,
}
}
pub fn len(&self) -> usize {
self.rows.len()
}
pub fn is_empty(&self) -> bool {
self.rows.is_empty()
}
pub fn one(self) -> Result<Row> {
match self.rows.len() {
0 => Err(QueryError::RowNotFound),
1 => Ok(self.rows.into_iter().next().unwrap()),
n => Err(QueryError::MultipleRowsFound { count: n }),
}
}
pub fn optional(self) -> Result<Option<Row>> {
match self.rows.len() {
0 => Ok(None),
1 => Ok(Some(self.rows.into_iter().next().unwrap())),
n => Err(QueryError::MultipleRowsFound { count: n }),
}
}
pub fn all(self) -> Vec<Row> {
self.rows
}
pub fn iter(&self) -> impl Iterator<Item = &Row> {
self.rows.iter()
}
}
impl Default for QueryResult {
fn default() -> Self {
Self::new()
}
}
impl IntoIterator for QueryResult {
type Item = Row;
type IntoIter = std::vec::IntoIter<Row>;
fn into_iter(self) -> Self::IntoIter {
self.rows.into_iter()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Row {
columns: HashMap<String, Value>,
}
impl Row {
pub fn new() -> Self {
Self {
columns: HashMap::new(),
}
}
pub fn from_map(columns: HashMap<String, Value>) -> Self {
Self { columns }
}
pub fn get(&self, column: &str) -> Option<&Value> {
self.columns.get(column)
}
pub fn get_required(&self, column: &str) -> Result<&Value> {
self.columns
.get(column)
.ok_or_else(|| QueryError::missing_field(column))
}
pub fn insert(&mut self, column: String, value: Value) {
self.columns.insert(column, value);
}
pub fn has_column(&self, column: &str) -> bool {
self.columns.contains_key(column)
}
pub fn columns(&self) -> Vec<&str> {
self.columns.keys().map(|s| s.as_str()).collect()
}
pub fn len(&self) -> usize {
self.columns.len()
}
pub fn is_empty(&self) -> bool {
self.columns.is_empty()
}
}
impl Default for Row {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Null,
Bool(bool),
Integer(i64),
Float(f64),
String(String),
Uuid(Uuid),
Timestamp(DateTime<Utc>),
Bytes(Vec<u8>),
Array(Vec<Value>),
Object(HashMap<String, Value>),
}
impl Value {
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
pub fn as_bool(&self) -> Result<bool> {
match self {
Value::Bool(b) => Ok(*b),
_ => Err(QueryError::type_mismatch("Bool", self.type_name())),
}
}
pub fn as_i64(&self) -> Result<i64> {
match self {
Value::Integer(i) => Ok(*i),
_ => Err(QueryError::type_mismatch("Integer", self.type_name())),
}
}
pub fn as_f64(&self) -> Result<f64> {
match self {
Value::Float(f) => Ok(*f),
Value::Integer(i) => Ok(*i as f64),
_ => Err(QueryError::type_mismatch("Float", self.type_name())),
}
}
pub fn as_str(&self) -> Result<&str> {
match self {
Value::String(s) => Ok(s.as_str()),
_ => Err(QueryError::type_mismatch("String", self.type_name())),
}
}
pub fn as_uuid(&self) -> Result<Uuid> {
match self {
Value::Uuid(u) => Ok(*u),
_ => Err(QueryError::type_mismatch("Uuid", self.type_name())),
}
}
pub fn as_timestamp(&self) -> Result<DateTime<Utc>> {
match self {
Value::Timestamp(t) => Ok(*t),
_ => Err(QueryError::type_mismatch("Timestamp", self.type_name())),
}
}
pub fn as_bytes(&self) -> Result<&Vec<u8>> {
match self {
Value::Bytes(b) => Ok(b),
_ => Err(QueryError::type_mismatch("Bytes", self.type_name())),
}
}
pub fn as_array(&self) -> Result<&Vec<Value>> {
match self {
Value::Array(a) => Ok(a),
_ => Err(QueryError::type_mismatch("Array", self.type_name())),
}
}
pub fn as_object(&self) -> Result<&HashMap<String, Value>> {
match self {
Value::Object(o) => Ok(o),
_ => Err(QueryError::type_mismatch("Object", self.type_name())),
}
}
pub fn type_name(&self) -> &str {
match self {
Value::Null => "Null",
Value::Bool(_) => "Bool",
Value::Integer(_) => "Integer",
Value::Float(_) => "Float",
Value::String(_) => "String",
Value::Bytes(_) => "Bytes",
Value::Uuid(_) => "Uuid",
Value::Timestamp(_) => "Timestamp",
Value::Array(_) => "Array",
Value::Object(_) => "Object",
}
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Value::Bool(b)
}
}
impl From<i64> for Value {
fn from(i: i64) -> Self {
Value::Integer(i)
}
}
impl From<i32> for Value {
fn from(i: i32) -> Self {
Value::Integer(i as i64)
}
}
impl From<f64> for Value {
fn from(f: f64) -> Self {
Value::Float(f)
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Value::String(s)
}
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
Value::String(s.to_string())
}
}
impl From<Uuid> for Value {
fn from(u: Uuid) -> Self {
Value::Uuid(u)
}
}
impl From<DateTime<Utc>> for Value {
fn from(t: DateTime<Utc>) -> Self {
Value::Timestamp(t)
}
}
impl From<Vec<u8>> for Value {
fn from(b: Vec<u8>) -> Self {
Value::Bytes(b)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_query_result_new() {
let result = QueryResult::new();
assert!(result.is_empty());
assert_eq!(result.len(), 0);
assert_eq!(result.affected_rows, 0);
}
#[test]
fn test_query_result_with_rows() {
let rows = vec![Row::new(), Row::new()];
let result = QueryResult::with_rows(rows);
assert_eq!(result.len(), 2);
assert!(!result.is_empty());
}
#[test]
fn test_query_result_one() {
let result = QueryResult::with_rows(vec![Row::new()]);
assert!(result.one().is_ok());
let empty = QueryResult::new();
assert!(empty.one().is_err());
let multiple = QueryResult::with_rows(vec![Row::new(), Row::new()]);
assert!(multiple.one().is_err());
}
#[test]
fn test_query_result_optional() {
let result = QueryResult::new();
assert_eq!(result.optional().unwrap(), None);
let one = QueryResult::with_rows(vec![Row::new()]);
assert!(one.optional().unwrap().is_some());
let multiple = QueryResult::with_rows(vec![Row::new(), Row::new()]);
assert!(multiple.optional().is_err());
}
#[test]
fn test_row_operations() {
let mut row = Row::new();
assert!(row.is_empty());
row.insert("id".to_string(), Value::Integer(1));
row.insert("name".to_string(), Value::String("Alice".to_string()));
assert_eq!(row.len(), 2);
assert!(row.get("id").is_some());
assert!(row.get("name").is_some());
assert!(row.get("missing").is_none());
}
#[test]
fn test_row_get_required() {
let mut row = Row::new();
row.insert("id".to_string(), Value::Integer(1));
assert!(row.get_required("id").is_ok());
assert!(row.get_required("missing").is_err());
}
#[test]
fn test_value_types() {
assert!(Value::Null.is_null());
assert!(!Value::Bool(true).is_null());
let bool_val = Value::Bool(true);
assert_eq!(bool_val.as_bool().unwrap(), true);
let int_val = Value::Integer(42);
assert_eq!(int_val.as_i64().unwrap(), 42);
let float_val = Value::Float(3.14);
assert!((float_val.as_f64().unwrap() - 3.14).abs() < 0.01);
let str_val = Value::String("hello".to_string());
assert_eq!(str_val.as_str().unwrap(), "hello");
}
#[test]
fn test_value_type_mismatch() {
let int_val = Value::Integer(42);
assert!(int_val.as_bool().is_err());
assert!(int_val.as_str().is_err());
let str_val = Value::String("hello".to_string());
assert!(str_val.as_i64().is_err());
}
#[test]
fn test_value_type_name() {
assert_eq!(Value::Null.type_name(), "Null");
assert_eq!(Value::Bool(true).type_name(), "Bool");
assert_eq!(Value::Integer(1).type_name(), "Integer");
assert_eq!(Value::Float(1.0).type_name(), "Float");
assert_eq!(Value::String("".to_string()).type_name(), "String");
assert_eq!(Value::Uuid(Uuid::new_v4()).type_name(), "Uuid");
}
#[test]
fn test_value_conversions() {
let bool_val: Value = true.into();
assert!(matches!(bool_val, Value::Bool(true)));
let int_val: Value = 42i64.into();
assert!(matches!(int_val, Value::Integer(42)));
let str_val: Value = "hello".into();
assert!(matches!(str_val, Value::String(_)));
let uuid = Uuid::new_v4();
let uuid_val: Value = uuid.into();
assert!(matches!(uuid_val, Value::Uuid(_)));
}
#[test]
fn test_value_float_from_int() {
let int_val = Value::Integer(42);
assert_eq!(int_val.as_f64().unwrap(), 42.0);
}
#[test]
fn test_value_array() {
let arr = Value::Array(vec![Value::Integer(1), Value::Integer(2)]);
let arr_ref = arr.as_array().unwrap();
assert_eq!(arr_ref.len(), 2);
}
#[test]
fn test_value_object() {
let mut map = HashMap::new();
map.insert("key".to_string(), Value::String("value".to_string()));
let obj = Value::Object(map);
let obj_ref = obj.as_object().unwrap();
assert_eq!(obj_ref.len(), 1);
}
}