nautilus-orm-core 0.1.3

Core query AST and type system for Nautilus ORM
Documentation
//! `FromValue` trait and standard scalar/collection implementations.

use crate::error::Result;
use crate::value::Value;

/// Trait for converting database values to Rust types.
///
/// This trait enables type-safe decoding of individual column values
/// during row deserialization in the selection API.
pub trait FromValue: Sized {
    /// Convert a Value reference to this type.
    ///
    /// Returns an error if the value is NULL, has the wrong type,
    /// or cannot be converted.
    fn from_value(value: &Value) -> Result<Self>;

    /// Convert an owned Value to this type, avoiding clones when possible.
    ///
    /// The default implementation delegates to `from_value`. Types that hold
    /// heap data (String, Json, Bytes) override this to take ownership
    /// instead of cloning.
    fn from_value_owned(value: Value) -> Result<Self> {
        Self::from_value(&value)
    }
}

impl FromValue for i64 {
    fn from_value(value: &Value) -> Result<Self> {
        match value {
            Value::I64(v) => Ok(*v),
            Value::Null => Err(crate::Error::TypeError("NULL value for i64".to_string())),
            _ => Err(crate::Error::TypeError(format!(
                "expected i64, got {:?}",
                value
            ))),
        }
    }
}

impl FromValue for i32 {
    fn from_value(value: &Value) -> Result<Self> {
        match value {
            Value::I32(v) => Ok(*v),
            Value::I64(v) => {
                // Allow conversion from I64 if it fits in i32
                (*v).try_into().map_err(|_| {
                    crate::Error::TypeError(format!("i64 value {} doesn't fit in i32", v))
                })
            }
            Value::Null => Err(crate::Error::TypeError("NULL value for i32".to_string())),
            _ => Err(crate::Error::TypeError(format!(
                "expected i32, got {:?}",
                value
            ))),
        }
    }
}

impl FromValue for String {
    fn from_value(value: &Value) -> Result<Self> {
        match value {
            Value::String(v) => Ok(v.clone()),
            Value::Null => Err(crate::Error::TypeError("NULL value for String".to_string())),
            _ => Err(crate::Error::TypeError(format!(
                "expected String, got {:?}",
                value
            ))),
        }
    }

    fn from_value_owned(value: Value) -> Result<Self> {
        match value {
            Value::String(v) => Ok(v),
            Value::Null => Err(crate::Error::TypeError("NULL value for String".to_string())),
            other => Err(crate::Error::TypeError(format!(
                "expected String, got {:?}",
                other
            ))),
        }
    }
}

impl FromValue for bool {
    fn from_value(value: &Value) -> Result<Self> {
        match value {
            Value::Bool(v) => Ok(*v),
            Value::Null => Err(crate::Error::TypeError("NULL value for bool".to_string())),
            _ => Err(crate::Error::TypeError(format!(
                "expected bool, got {:?}",
                value
            ))),
        }
    }
}

impl FromValue for f64 {
    fn from_value(value: &Value) -> Result<Self> {
        match value {
            Value::F64(v) => Ok(*v),
            Value::Null => Err(crate::Error::TypeError("NULL value for f64".to_string())),
            _ => Err(crate::Error::TypeError(format!(
                "expected f64, got {:?}",
                value
            ))),
        }
    }
}

impl FromValue for rust_decimal::Decimal {
    fn from_value(value: &Value) -> Result<Self> {
        match value {
            Value::Decimal(v) => Ok(*v),
            Value::Null => Err(crate::Error::TypeError(
                "NULL value for Decimal".to_string(),
            )),
            _ => Err(crate::Error::TypeError(format!(
                "expected Decimal, got {:?}",
                value
            ))),
        }
    }
}

impl FromValue for chrono::NaiveDateTime {
    fn from_value(value: &Value) -> Result<Self> {
        match value {
            Value::DateTime(v) => Ok(*v),
            Value::Null => Err(crate::Error::TypeError(
                "NULL value for DateTime".to_string(),
            )),
            _ => Err(crate::Error::TypeError(format!(
                "expected DateTime, got {:?}",
                value
            ))),
        }
    }
}

impl FromValue for uuid::Uuid {
    fn from_value(value: &Value) -> Result<Self> {
        match value {
            Value::Uuid(v) => Ok(*v),
            Value::Null => Err(crate::Error::TypeError("NULL value for Uuid".to_string())),
            _ => Err(crate::Error::TypeError(format!(
                "expected Uuid, got {:?}",
                value
            ))),
        }
    }
}

impl FromValue for serde_json::Value {
    fn from_value(value: &Value) -> Result<Self> {
        match value {
            Value::Json(v) => Ok(v.clone()),
            Value::Null => Err(crate::Error::TypeError("NULL value for Json".to_string())),
            _ => Err(crate::Error::TypeError(format!(
                "expected Json, got {:?}",
                value
            ))),
        }
    }

    fn from_value_owned(value: Value) -> Result<Self> {
        match value {
            Value::Json(v) => Ok(v),
            Value::Null => Err(crate::Error::TypeError("NULL value for Json".to_string())),
            other => Err(crate::Error::TypeError(format!(
                "expected Json, got {:?}",
                other
            ))),
        }
    }
}

impl FromValue for Vec<u8> {
    fn from_value(value: &Value) -> Result<Self> {
        match value {
            Value::Bytes(v) => Ok(v.clone()),
            Value::Null => Err(crate::Error::TypeError("NULL value for Bytes".to_string())),
            _ => Err(crate::Error::TypeError(format!(
                "expected Bytes, got {:?}",
                value
            ))),
        }
    }

    fn from_value_owned(value: Value) -> Result<Self> {
        match value {
            Value::Bytes(v) => Ok(v),
            Value::Null => Err(crate::Error::TypeError("NULL value for Bytes".to_string())),
            other => Err(crate::Error::TypeError(format!(
                "expected Bytes, got {:?}",
                other
            ))),
        }
    }
}

// Generic Option<T> implementation for nullable fields
impl<T: FromValue> FromValue for Option<T> {
    fn from_value(value: &Value) -> Result<Self> {
        match value {
            Value::Null => Ok(None),
            _ => T::from_value(value).map(Some),
        }
    }

    fn from_value_owned(value: Value) -> Result<Self> {
        match value {
            Value::Null => Ok(None),
            _ => T::from_value_owned(value).map(Some),
        }
    }
}

// Array field implementations for common types

/// Generates `FromValue` implementations for `Vec<T>` and `Vec<Vec<T>>` for
/// scalar types that already implement `FromValue`.
///
/// Both variants support:
/// - Native PostgreSQL array values (`Value::Array` / `Value::Array2D`)
/// - JSON-encoded arrays from MySQL and SQLite (`Value::Json`)
macro_rules! impl_vec_from_value {
    ($T:ty) => {
        impl FromValue for Vec<$T> {
            fn from_value(value: &Value) -> Result<Self> {
                match value {
                    Value::Array(items) => items.iter().map(<$T>::from_value).collect(),
                    Value::Json(json_value) => decode_json_array(json_value, <$T>::from_value),
                    Value::Null => Err(crate::Error::TypeError(
                        concat!("NULL value for Vec<", stringify!($T), ">").to_string(),
                    )),
                    _ => Err(crate::Error::TypeError(format!(
                        concat!(
                            "expected Array or Json for Vec<",
                            stringify!($T),
                            ">, got {:?}"
                        ),
                        value
                    ))),
                }
            }
        }

        impl FromValue for Vec<Vec<$T>> {
            fn from_value(value: &Value) -> Result<Self> {
                match value {
                    Value::Array2D(rows) => rows
                        .iter()
                        .map(|row| row.iter().map(<$T>::from_value).collect())
                        .collect(),
                    Value::Json(json_value) => decode_json_2d_array(json_value, <$T>::from_value),
                    Value::Null => Err(crate::Error::TypeError(
                        concat!("NULL value for Vec<Vec<", stringify!($T), ">>").to_string(),
                    )),
                    _ => Err(crate::Error::TypeError(format!(
                        concat!(
                            "expected Array2D or Json for Vec<Vec<",
                            stringify!($T),
                            ">>, got {:?}"
                        ),
                        value
                    ))),
                }
            }
        }
    };
}

impl_vec_from_value!(String);
impl_vec_from_value!(i32);
impl_vec_from_value!(i64);
impl_vec_from_value!(f64);
impl_vec_from_value!(bool);

// Helper function to decode JSON arrays
fn decode_json_array<T, F>(json_value: &serde_json::Value, decoder: F) -> Result<Vec<T>>
where
    F: Fn(&Value) -> Result<T>,
{
    if let serde_json::Value::Array(arr) = json_value {
        let mut result = Vec::with_capacity(arr.len());
        for json_item in arr {
            // Delegate to the canonical infallible converter in value.rs
            let value = crate::value::json_to_value_ref(json_item);
            result.push(decoder(&value)?);
        }
        Ok(result)
    } else {
        Err(crate::Error::TypeError(format!(
            "expected JSON array, got {:?}",
            json_value
        )))
    }
}

// Helper function to decode 2D JSON arrays
fn decode_json_2d_array<T, F>(json_value: &serde_json::Value, decoder: F) -> Result<Vec<Vec<T>>>
where
    F: Fn(&Value) -> Result<T>,
{
    if let serde_json::Value::Array(outer_arr) = json_value {
        let mut result = Vec::with_capacity(outer_arr.len());
        for json_row in outer_arr {
            if let serde_json::Value::Array(inner_arr) = json_row {
                let mut row = Vec::with_capacity(inner_arr.len());
                for json_item in inner_arr {
                    let value = crate::value::json_to_value_ref(json_item);
                    row.push(decoder(&value)?);
                }
                result.push(row);
            } else {
                return Err(crate::Error::TypeError(format!(
                    "expected inner JSON array, got {:?}",
                    json_row
                )));
            }
        }
        Ok(result)
    } else {
        Err(crate::Error::TypeError(format!(
            "expected JSON 2D array, got {:?}",
            json_value
        )))
    }
}