#![allow(non_snake_case)]
use crate::collection::{Document, NitriteId};
use crate::common::{ReadExecutor, Value};
use crate::errors::{ErrorKind, NitriteError, NitriteResult};
use crate::{atomic, document_from_map, Atomic};
use std::any::{Any, TypeId};
use std::collections::{BTreeMap, HashMap, HashSet};
use std::hash::Hash;
use std::str::FromStr;
pub trait Convertible {
type Output;
fn to_value(&self) -> NitriteResult<Value>;
fn from_value(value: &Value) -> NitriteResult<Self::Output>;
}
impl Convertible for i8 {
type Output = i8;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::I8(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::I8(i) => Ok(*i),
_ => {
log::error!("Value {} is not an i8", value);
Err(NitriteError::new(
"Value is not an i8",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for i16 {
type Output = i16;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::I16(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::I16(i) => Ok(*i),
_ => {
log::error!("Value {} is not an i16", value);
Err(NitriteError::new(
"Value is not an i16",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for i32 {
type Output = i32;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::I32(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::I32(i) => Ok(*i),
_ => {
log::error!("Value {} is not an i32", value);
Err(NitriteError::new(
"Value is not an i32",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for i64 {
type Output = i64;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::I64(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::I64(i) => Ok(*i),
_ => {
log::error!("Value {} is not an i64", value);
Err(NitriteError::new(
"Value is not an i64",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for i128 {
type Output = i128;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::I128(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::I128(i) => Ok(*i),
_ => {
log::error!("Value {} is not an i128", value);
Err(NitriteError::new(
"Value is not an i128",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for u8 {
type Output = u8;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::U8(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::U8(i) => Ok(*i),
_ => {
log::error!("Value {} is not a u8", value);
Err(NitriteError::new(
"Value is not a u8",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for u16 {
type Output = u16;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::U16(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::U16(i) => Ok(*i),
_ => {
log::error!("Value {} is not a u16", value);
Err(NitriteError::new(
"Value is not a u16",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for u32 {
type Output = u32;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::U32(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::U32(i) => Ok(*i),
_ => {
log::error!("Value {} is not a u32", value);
Err(NitriteError::new(
"Value is not a u32",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for u64 {
type Output = u64;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::U64(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::U64(i) => Ok(*i),
_ => {
log::error!("Value {} is not a u64", value);
Err(NitriteError::new(
"Value is not a u64",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for u128 {
type Output = u128;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::U128(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::U128(i) => Ok(*i),
_ => {
log::error!("Value {} is not a u128", value);
Err(NitriteError::new(
"Value is not a u128",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for f32 {
type Output = f32;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::F32(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::F32(i) => Ok(*i),
_ => {
log::error!("Value {} is not a f32", value);
Err(NitriteError::new(
"Value is not a f32",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for f64 {
type Output = f64;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::F64(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::F64(i) => Ok(*i),
_ => {
log::error!("Value {} is not a f64", value);
Err(NitriteError::new(
"Value is not a f64",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for char {
type Output = char;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::Char(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::Char(i) => Ok(*i),
_ => {
log::error!("Value {} is not a char", value);
Err(NitriteError::new(
"Value is not a char",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for String {
type Output = String;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::String(self.clone()))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::String(i) => Ok(i.clone()),
_ => {
log::error!("Value {} is not a string", value);
Err(NitriteError::new(
"Value is not a string",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for &str {
type Output = String;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::String(self.to_string()))
}
fn from_value(value: &Value) -> NitriteResult<Self::Output> {
match value {
Value::String(i) => Ok(i.to_owned()),
_ => {
log::error!("Value {} is not a string", value);
Err(NitriteError::new(
"Value is not a string",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for bool {
type Output = bool;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::Bool(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::Bool(i) => Ok(*i),
_ => {
log::error!("Value {} is not a bool", value);
Err(NitriteError::new(
"Value is not a bool",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for () {
type Output = ();
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::Null)
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::Null => Ok(()),
_ => {
log::error!("Value {} is not a null", value);
Err(NitriteError::new(
"Value is not a null",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for NitriteId {
type Output = NitriteId;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::NitriteId(*self))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::NitriteId(i) => Ok(*i),
_ => {
log::error!("Value {} is not a nitrite id", value);
Err(NitriteError::new(
"Value is not a nitrite id",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for Document {
type Output = Document;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::Document(self.clone()))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::Document(i) => Ok(i.clone()),
_ => {
log::error!("Value {} is not a document", value);
Err(NitriteError::new(
"Value is not a document",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for BTreeMap<Value, Value> {
type Output = BTreeMap<Value, Value>;
fn to_value(&self) -> NitriteResult<Value> {
Ok(Value::Map(self.clone()))
}
fn from_value(value: &Value) -> NitriteResult<Self> {
match value {
Value::Map(i) => Ok(i.clone()),
_ => {
log::error!("Value {} is not a map", value);
Err(NitriteError::new(
"Value is not a map",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl<T> Convertible for Option<T>
where
T: Convertible,
{
type Output = Option<T::Output>;
fn to_value(&self) -> NitriteResult<Value> {
match self {
Some(v) => v.to_value(),
None => Ok(Value::Null),
}
}
fn from_value(value: &Value) -> NitriteResult<Self::Output> {
match value {
Value::Null => Ok(None),
_ => Ok(Some(T::from_value(value)?)),
}
}
}
impl<T> Convertible for Box<T>
where
T: Convertible,
{
type Output = Box<T::Output>;
fn to_value(&self) -> NitriteResult<Value> {
self.as_ref().to_value()
}
fn from_value(value: &Value) -> NitriteResult<Self::Output> {
Ok(Box::new(T::from_value(value)?))
}
}
impl<T> Convertible for Atomic<T>
where
T: Convertible,
{
type Output = Atomic<T::Output>;
fn to_value(&self) -> NitriteResult<Value> {
self.read_with(|it| it.to_value())
}
fn from_value(value: &Value) -> NitriteResult<Self::Output> {
Ok(atomic(T::from_value(value)?))
}
}
impl<T> Convertible for Vec<T>
where
T: Convertible + Any,
{
type Output = Vec<T::Output>;
fn to_value(&self) -> NitriteResult<Value> {
if TypeId::of::<T>() == TypeId::of::<u8>() {
let mut arr = Vec::new();
for item in self {
let raw = item.to_value()?;
if let Value::U8(item) = raw {
arr.push(item);
} else {
log::error!("Value {} is not a u8", raw);
return Err(NitriteError::new(
"Value is not a u8",
ErrorKind::ObjectMappingError,
));
}
}
return Ok(Value::Bytes(arr));
}
let mut arr = Vec::new();
for item in self {
arr.push(item.to_value()?);
}
Ok(Value::Array(arr))
}
fn from_value(value: &Value) -> NitriteResult<Self::Output> {
match value {
Value::Bytes(arr) => {
let mut vec = Vec::new();
for item in arr {
vec.push(T::from_value(&Value::U8(*item))?);
}
Ok(vec)
}
Value::Array(arr) => {
let mut vec = Vec::new();
for item in arr {
vec.push(T::from_value(item)?);
}
Ok(vec)
}
_ => {
log::error!("Value {} is not an array", value);
Err(NitriteError::new(
"Value is not an array",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl<K, V> Convertible for BTreeMap<K, V>
where
K: ToString + FromStr + Ord,
V: Convertible,
{
type Output = BTreeMap<K, V::Output>;
fn to_value(&self) -> NitriteResult<Value> {
let mut map = BTreeMap::new();
for (k, v) in self {
map.insert(k.to_string(), v.to_value()?);
}
Ok(Value::Document(document_from_map(&map)?))
}
fn from_value(value: &Value) -> NitriteResult<Self::Output> {
match value {
Value::Document(doc) => {
let mut result = BTreeMap::new();
for (k, v) in doc.iter() {
match K::from_str(k.as_str()) {
Ok(key) => {
result.insert(key, V::from_value(&v)?);
}
Err(_) => {
log::error!("Failed to convert key {} to type", k);
return Err(NitriteError::new(
&format!("Failed to convert key {} to type", k),
ErrorKind::ObjectMappingError,
));
}
}
}
Ok(result)
}
_ => {
log::error!("Value is not a document");
Err(NitriteError::new(
"Value is not a document",
ErrorKind::ObjectMappingError,
))
}
}
}
}
impl<K, V> Convertible for HashMap<K, V>
where
K: ToString + FromStr + Eq + Hash,
V: Convertible,
{
type Output = HashMap<K, V::Output>;
fn to_value(&self) -> NitriteResult<Value> {
let mut map = BTreeMap::new();
for (k, v) in self {
map.insert(k.to_string(), v.to_value()?);
}
Ok(Value::Document(document_from_map(&map)?))
}
fn from_value(value: &Value) -> NitriteResult<Self::Output> {
match value {
Value::Document(doc) => {
let mut map = HashMap::new();
for (k, v) in doc.iter() {
let key = K::from_str(&k);
match key {
Ok(k) => {
map.insert(k, V::from_value(&v)?);
}
Err(_) => {
log::error!("Key is not a valid string: {}", k);
return Err(NitriteError::new(
"Key is not a valid string",
ErrorKind::ObjectMappingError,
));
}
}
}
Ok(map)
}
_ => {
log::error!("Value {} is not a document", value);
Err(NitriteError::new(
"Value is not a document",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl<V> Convertible for HashSet<V>
where
V: Convertible,
V::Output: Eq + Hash,
{
type Output = HashSet<V::Output>;
fn to_value(&self) -> NitriteResult<Value> {
let mut array = Vec::new();
for item in self {
array.push(item.to_value()?);
}
Ok(Value::Array(array))
}
fn from_value(value: &Value) -> NitriteResult<Self::Output> {
match value {
Value::Array(arr) => {
let mut set = HashSet::new();
for item in arr {
set.insert(V::from_value(item)?);
}
Ok(set)
}
_ => {
log::error!("Value {} is not an array", value);
Err(NitriteError::new(
"Value is not an array",
ErrorKind::ObjectMappingError,
))
},
}
}
}
impl Convertible for Value {
type Output = Value;
fn to_value(&self) -> NitriteResult<Value> {
Ok(self.clone())
}
fn from_value(value: &Value) -> NitriteResult<Self> {
Ok(value.clone())
}
}
macro_rules! impl_convertible_for_tuples {
($(($($T:ident),+)),+) => {
$(
impl<$($T),+> Convertible for ($($T),+) where $($T: Convertible),+ {
type Output = ($($T::Output),+);
fn to_value(&self) -> NitriteResult<Value> {
let ($($T),+) = self;
Ok(Value::Array(vec![$($T.to_value()?),+]))
}
fn from_value(value: &Value) -> NitriteResult<Self::Output> {
match value {
Value::Array(arr) => {
if arr.len() != count_idents!($($T),+) {
log::error!("Value is not a tuple");
return Err(NitriteError::new(
"Value is not a tuple",
ErrorKind::ObjectMappingError,
));
}
let mut iter = arr.iter();
Ok(($($T::from_value(iter.next().ok_or_else(|| NitriteError::new(
"Tuple element missing",
ErrorKind::ObjectMappingError,
))?)?),+))
}
_ => {
log::error!("Value {} is not an array", value);
Err(NitriteError::new(
"Value is not an array",
ErrorKind::ObjectMappingError,
))
}
}
}
}
)+
};
}
macro_rules! count_idents {
($($idents:ident),*) => {
<[()]>::len(&[$(count_idents!(@sub $idents)),*])
};
(@sub $idents:ident) => { () };
}
impl_convertible_for_tuples! {
(T1, T2),
(T1, T2, T3),
(T1, T2, T3, T4),
(T1, T2, T3, T4, T5),
(T1, T2, T3, T4, T5, T6),
(T1, T2, T3, T4, T5, T6, T7),
(T1, T2, T3, T4, T5, T6, T7, T8),
(T1, T2, T3, T4, T5, T6, T7, T8, T9),
(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
}
pub fn from_value<T>(value: &Value) -> NitriteResult<T::Output>
where
T: Convertible,
{
T::from_value(value)
}
pub fn to_value<T>(data: &T) -> NitriteResult<Value>
where
T: Convertible,
{
data.to_value()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::Value;
use std::collections::{BTreeMap, HashMap, HashSet};
fn test_convertible<T>(value: T, expected: Value)
where
T: Convertible<Output = T> + PartialEq + std::fmt::Debug,
T::Output: PartialEq + std::fmt::Debug,
{
let to_value = value.to_value().unwrap();
assert_eq!(to_value, expected);
let from_value = T::from_value(&expected).unwrap();
assert_eq!(from_value, value);
}
fn test_convertible_error<T>(value: &Value, expected_error: &str)
where
T: Convertible, <T as Convertible>::Output: std::fmt::Debug
{
let result = T::from_value(value);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(error.to_string(), expected_error);
}
#[test]
fn test_i8() {
test_convertible(42_i8, Value::I8(42));
test_convertible_error::<i8>(&Value::I16(42), "Value is not an i8");
}
#[test]
fn test_i16() {
test_convertible(42_i16, Value::I16(42));
test_convertible_error::<i16>(&Value::I32(42), "Value is not an i16");
}
#[test]
fn test_i32() {
test_convertible(42_i32, Value::I32(42));
test_convertible_error::<i32>(&Value::I64(42), "Value is not an i32");
}
#[test]
fn test_i64() {
test_convertible(42_i64, Value::I64(42));
test_convertible_error::<i64>(&Value::I128(42), "Value is not an i64");
}
#[test]
fn test_i128() {
test_convertible(42_i128, Value::I128(42));
test_convertible_error::<i128>(&Value::U8(42), "Value is not an i128");
}
#[test]
fn test_u8() {
test_convertible(42_u8, Value::U8(42));
test_convertible_error::<u8>(&Value::U16(42), "Value is not a u8");
}
#[test]
fn test_u16() {
test_convertible(42_u16, Value::U16(42));
test_convertible_error::<u16>(&Value::U32(42), "Value is not a u16");
}
#[test]
fn test_u32() {
test_convertible(42_u32, Value::U32(42));
test_convertible_error::<u32>(&Value::U64(42), "Value is not a u32");
}
#[test]
fn test_u64() {
test_convertible(42_u64, Value::U64(42));
test_convertible_error::<u64>(&Value::U128(42), "Value is not a u64");
}
#[test]
fn test_u128() {
test_convertible(42_u128, Value::U128(42));
test_convertible_error::<u128>(&Value::F32(42.0), "Value is not a u128");
}
#[test]
fn test_f32() {
test_convertible(42.0_f32, Value::F32(42.0));
test_convertible_error::<f32>(&Value::F64(42.0), "Value is not a f32");
}
#[test]
fn test_f64() {
test_convertible(42.0_f64, Value::F64(42.0));
test_convertible_error::<f64>(&Value::Char('a'), "Value is not a f64");
}
#[test]
fn test_char() {
test_convertible('a', Value::Char('a'));
test_convertible_error::<char>(&Value::String("a".to_string()), "Value is not a char");
}
#[test]
fn test_string() {
test_convertible("hello".to_string(), Value::String("hello".to_string()));
test_convertible_error::<String>(&Value::Bool(true), "Value is not a string");
}
#[test]
fn test_str() {
let value: &str = "hello";
let expected = Value::String("hello".to_string());
let to_value = value.to_value().unwrap();
assert_eq!(to_value, expected);
let from_value = <&str>::from_value(&expected).unwrap();
assert_eq!(from_value, "hello".to_string());
test_convertible_error::<&str>(&Value::Bool(true), "Value is not a string");
}
#[test]
fn test_bool() {
test_convertible(true, Value::Bool(true));
test_convertible_error::<bool>(&Value::I32(42), "Value is not a bool");
}
#[test]
fn test_unit() {
test_convertible((), Value::Null);
test_convertible_error::<()>(&Value::I32(42), "Value is not a null");
}
#[test]
fn test_option() {
let value: Option<i32> = Some(42);
let expected = Value::I32(42);
let to_value = value.to_value().unwrap();
assert_eq!(to_value, expected);
let from_value = Option::<i32>::from_value(&expected).unwrap();
assert_eq!(from_value, Some(42));
let value: Option<i32> = None;
let expected = Value::Null;
let to_value = value.to_value().unwrap();
assert_eq!(to_value, expected);
let from_value = Option::<i32>::from_value(&expected).unwrap();
assert_eq!(from_value, None);
test_convertible_error::<Option<i32>>(&Value::Bool(true), "Value is not an i32");
}
#[test]
fn test_box() {
let value: Box<i32> = Box::new(42);
let expected = Value::I32(42);
let to_value = value.to_value().unwrap();
assert_eq!(to_value, expected);
let from_value = Box::<i32>::from_value(&expected).unwrap();
assert_eq!(from_value, Box::new(42));
test_convertible_error::<Box<i32>>(&Value::Bool(true), "Value is not an i32");
}
#[test]
fn test_vec() {
let value: Vec<i32> = vec![1, 2, 3];
let expected = Value::Array(vec![Value::I32(1), Value::I32(2), Value::I32(3)]);
let to_value = value.to_value().unwrap();
assert_eq!(to_value, expected);
let from_value = Vec::<i32>::from_value(&expected).unwrap();
assert_eq!(from_value, vec![1, 2, 3]);
test_convertible_error::<Vec<i32>>(&Value::Bool(true), "Value is not an array");
}
#[test]
fn test_btreemap() {
let mut value = BTreeMap::new();
value.insert("key".to_string(), 42);
let mut expected = BTreeMap::new();
expected.insert("key".to_string(), Value::I32(42));
let to_value = value.to_value().unwrap();
assert_eq!(to_value, Value::Document(document_from_map(&expected).unwrap()));
let from_value = BTreeMap::<String, i32>::from_value(&to_value).unwrap();
assert_eq!(from_value, value);
test_convertible_error::<BTreeMap<String, i32>>(&Value::Bool(true), "Value is not a document");
}
#[test]
fn test_hashmap() {
let mut value = HashMap::new();
value.insert("key".to_string(), 42);
let mut expected = BTreeMap::new();
expected.insert("key".to_string(), Value::I32(42));
let to_value = value.to_value().unwrap();
assert_eq!(to_value, Value::Document(document_from_map(&expected).unwrap()));
let from_value = HashMap::<String, i32>::from_value(&to_value).unwrap();
assert_eq!(from_value, value);
test_convertible_error::<HashMap<String, i32>>(&Value::Bool(true), "Value is not a document");
}
#[test]
fn test_hashset() {
let mut value = HashSet::new();
value.insert(42);
let expected = Value::Array(vec![Value::I32(42)]);
let to_value = value.to_value().unwrap();
assert_eq!(to_value, expected);
let from_value = HashSet::<i32>::from_value(&expected).unwrap();
assert_eq!(from_value, value);
test_convertible_error::<HashSet<i32>>(&Value::Bool(true), "Value is not an array");
}
#[test]
fn test_value() {
let value = Value::I32(42);
let to_value = value.to_value().unwrap();
assert_eq!(to_value, value);
let from_value = Value::from_value(&value).unwrap();
assert_eq!(from_value, value);
}
#[test]
fn test_tuple() {
let value = (1, "hello".to_string());
let expected = Value::Array(vec![Value::I32(1), Value::String("hello".to_string())]);
let to_value = value.to_value().unwrap();
assert_eq!(to_value, expected);
let from_value = <(i32, String)>::from_value(&expected).unwrap();
assert_eq!(from_value, value);
let value = (1, "hello".to_string(), 3.14);
let expected = Value::Array(vec![Value::I32(1), Value::String("hello".to_string()), Value::F64(3.14)]);
let to_value = value.to_value().unwrap();
assert_eq!(to_value, expected);
let from_value = <(i32, String, f64)>::from_value(&expected).unwrap();
assert_eq!(from_value, value);
test_convertible_error::<(i32, String)>(&Value::Bool(true), "Value is not an array");
}
#[test]
fn test_tuple_with_length_mismatch() {
let short_array = Value::Array(vec![Value::I32(1)]);
let result = <(i32, String)>::from_value(&short_array);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(error.to_string(), "Value is not a tuple");
}
#[test]
fn test_tuple_with_length_mismatch_too_many_elements() {
let long_array = Value::Array(vec![
Value::I32(1),
Value::String("hello".to_string()),
Value::F64(3.14),
]);
let result = <(i32, String)>::from_value(&long_array);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(error.to_string(), "Value is not a tuple");
}
#[test]
fn test_tuple_with_type_conversion_error() {
let wrong_type_array = Value::Array(vec![
Value::String("not_an_int".to_string()), Value::String("hello".to_string()),
]);
let result = <(i32, String)>::from_value(&wrong_type_array);
assert!(result.is_err());
}
#[test]
fn test_tuple_three_elements_with_length_mismatch() {
let short_array = Value::Array(vec![Value::I32(1), Value::String("hello".to_string())]);
let result = <(i32, String, f64)>::from_value(&short_array);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(error.to_string(), "Value is not a tuple");
}
#[test]
fn test_tuple_three_elements_with_wrong_element_type() {
let wrong_type_array = Value::Array(vec![
Value::I32(1),
Value::String("hello".to_string()),
Value::Bool(true), ]);
let result = <(i32, String, f64)>::from_value(&wrong_type_array);
assert!(result.is_err());
}
#[test]
fn test_tuple_roundtrip_multiple_types() {
let original = (42_i32, "test".to_string(), 3.14_f64, true);
let serialized = original.to_value().unwrap();
let deserialized = <(i32, String, f64, bool)>::from_value(&serialized).unwrap();
assert_eq!(original, deserialized);
}
#[test]
fn test_tuple_nested_in_array() {
let tuple_values = vec![(1_i32, "a".to_string()), (2_i32, "b".to_string())];
let expected = Value::Array(vec![
Value::Array(vec![Value::I32(1), Value::String("a".to_string())]),
Value::Array(vec![Value::I32(2), Value::String("b".to_string())]),
]);
let serialized = tuple_values.to_value().unwrap();
assert_eq!(serialized, expected);
}
#[test]
fn test_tuple_empty_array_conversion() {
let empty_array = Value::Array(vec![]);
let result = <(i32, String)>::from_value(&empty_array);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(error.to_string(), "Value is not a tuple");
}
}