use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Null,
Bool(bool),
I32(i32),
I64(i64),
F64(f64),
Decimal(rust_decimal::Decimal),
DateTime(chrono::NaiveDateTime),
Uuid(uuid::Uuid),
Json(serde_json::Value),
String(String),
Bytes(Vec<u8>),
Array(Vec<Value>),
Array2D(Vec<Vec<Value>>),
Enum {
value: String,
type_name: String,
},
}
impl From<bool> for Value {
fn from(v: bool) -> Self {
Value::Bool(v)
}
}
impl From<i32> for Value {
fn from(v: i32) -> Self {
Value::I32(v)
}
}
impl From<i64> for Value {
fn from(v: i64) -> Self {
Value::I64(v)
}
}
impl From<f64> for Value {
fn from(v: f64) -> Self {
Value::F64(v)
}
}
impl From<rust_decimal::Decimal> for Value {
fn from(v: rust_decimal::Decimal) -> Self {
Value::Decimal(v)
}
}
impl From<chrono::NaiveDateTime> for Value {
fn from(v: chrono::NaiveDateTime) -> Self {
Value::DateTime(v)
}
}
impl From<uuid::Uuid> for Value {
fn from(v: uuid::Uuid) -> Self {
Value::Uuid(v)
}
}
impl From<serde_json::Value> for Value {
fn from(v: serde_json::Value) -> Self {
Value::Json(v)
}
}
impl From<String> for Value {
fn from(v: String) -> Self {
Value::String(v)
}
}
impl From<&str> for Value {
fn from(v: &str) -> Self {
Value::String(v.to_string())
}
}
impl From<Vec<u8>> for Value {
fn from(v: Vec<u8>) -> Self {
Value::Bytes(v)
}
}
macro_rules! impl_vec_from {
($($t:ty),* $(,)?) => {
$(
impl From<Vec<$t>> for Value {
fn from(v: Vec<$t>) -> Self {
Value::Array(v.into_iter().map(|x| x.into()).collect())
}
}
impl From<Vec<Vec<$t>>> for Value {
fn from(v: Vec<Vec<$t>>) -> Self {
Value::Array2D(
v.into_iter()
.map(|row| row.into_iter().map(|x| x.into()).collect())
.collect(),
)
}
}
)*
};
}
impl_vec_from!(
i32,
i64,
f64,
bool,
String,
rust_decimal::Decimal,
uuid::Uuid,
chrono::NaiveDateTime,
serde_json::Value,
);
macro_rules! impl_option_from {
($($t:ty),* $(,)?) => {
$(
impl From<Option<$t>> for Value {
fn from(v: Option<$t>) -> Self {
v.map(|x| x.into()).unwrap_or(Value::Null)
}
}
)*
};
}
impl_option_from!(
bool,
i32,
i64,
f64,
String,
rust_decimal::Decimal,
uuid::Uuid,
chrono::NaiveDateTime,
);
impl From<Option<&str>> for Value {
fn from(v: Option<&str>) -> Self {
v.map(|s| Value::String(s.to_string()))
.unwrap_or(Value::Null)
}
}
impl Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Value::Null => serializer.serialize_none(),
Value::Bool(b) => serializer.serialize_bool(*b),
Value::I32(i) => serializer.serialize_i32(*i),
Value::I64(i) => serializer.serialize_i64(*i),
Value::F64(f) => serializer.serialize_f64(*f),
Value::Decimal(d) => serializer.serialize_str(&d.to_string()),
Value::DateTime(dt) => {
serializer.serialize_str(&dt.format("%Y-%m-%dT%H:%M:%S%.fZ").to_string())
}
Value::Uuid(u) => serializer.serialize_str(&u.to_string()),
Value::Bytes(b) => {
use base64::Engine;
serializer.serialize_str(&base64::engine::general_purpose::STANDARD.encode(b))
}
Value::Json(j) => j.serialize(serializer),
Value::String(s) => serializer.serialize_str(s),
Value::Array(arr) => {
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(Some(arr.len()))?;
for item in arr {
seq.serialize_element(item)?;
}
seq.end()
}
Value::Array2D(arr2d) => {
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(Some(arr2d.len()))?;
for row in arr2d {
let row_values: Vec<_> = row.iter().collect();
seq.serialize_element(&row_values)?;
}
seq.end()
}
Value::Enum { value, .. } => serializer.serialize_str(value),
}
}
}
impl<'de> Deserialize<'de> for Value {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let json: serde_json::Value = Deserialize::deserialize(deserializer)?;
Ok(json_to_value_ref(&json))
}
}
pub(crate) fn json_to_value_ref(json: &serde_json::Value) -> Value {
match json {
serde_json::Value::Null => Value::Null,
serde_json::Value::Bool(b) => Value::Bool(*b),
serde_json::Value::Number(n) => {
if let Some(i) = n.as_i64() {
if i >= i32::MIN as i64 && i <= i32::MAX as i64 {
Value::I32(i as i32)
} else {
Value::I64(i)
}
} else if let Some(f) = n.as_f64() {
Value::F64(f)
} else {
Value::String(n.to_string())
}
}
serde_json::Value::String(s) => Value::String(s.clone()),
serde_json::Value::Array(arr) => Value::Array(arr.iter().map(json_to_value_ref).collect()),
serde_json::Value::Object(_) => Value::Json(json.clone()),
}
}
#[cfg(test)]
mod tests {
use core::f64;
use super::*;
#[test]
fn test_value_variants() {
assert_eq!(Value::Null, Value::Null);
assert_eq!(Value::Bool(true), Value::from(true));
assert_eq!(Value::I32(42), Value::from(42i32));
assert_eq!(Value::I64(42), Value::from(42i64));
assert_eq!(Value::F64(2.5), Value::from(2.5f64));
assert_eq!(Value::String("hello".to_string()), Value::from("hello"));
assert_eq!(Value::Bytes(vec![1, 2, 3]), Value::from(vec![1u8, 2, 3]));
use rust_decimal::Decimal;
let dec = Decimal::new(12345, 2); assert_eq!(Value::Decimal(dec), Value::from(dec));
use chrono::NaiveDate;
let dt = NaiveDate::from_ymd_opt(2024, 1, 1)
.unwrap()
.and_hms_opt(12, 0, 0)
.unwrap();
assert_eq!(Value::DateTime(dt), Value::from(dt));
use uuid::Uuid;
let id = Uuid::nil();
assert_eq!(Value::Uuid(id), Value::from(id));
use serde_json::json;
let j = json!({"key": "value"});
assert_eq!(Value::Json(j.clone()), Value::from(j));
}
#[test]
fn test_value_serialization_null() {
let value = Value::Null;
let json = serde_json::to_value(&value).unwrap();
assert_eq!(json, serde_json::Value::Null);
let deserialized: Value = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, Value::Null);
}
#[test]
fn test_value_serialization_bool() {
let value = Value::Bool(true);
let json = serde_json::to_value(&value).unwrap();
assert_eq!(json, serde_json::Value::Bool(true));
let deserialized: Value = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, Value::Bool(true));
}
#[test]
fn test_value_serialization_numbers() {
let value = Value::I32(42);
let json = serde_json::to_value(&value).unwrap();
assert_eq!(json.as_i64(), Some(42));
let value = Value::I64(9007199254740991);
let json = serde_json::to_value(&value).unwrap();
assert_eq!(json.as_i64(), Some(9007199254740991));
let value = Value::F64(f64::consts::PI);
let json = serde_json::to_value(&value).unwrap();
assert_eq!(json.as_f64(), Some(f64::consts::PI));
}
#[test]
fn test_value_serialization_string() {
let value = Value::String("hello world".to_string());
let json = serde_json::to_value(&value).unwrap();
assert_eq!(json.as_str(), Some("hello world"));
let deserialized: Value = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, Value::String("hello world".to_string()));
}
#[test]
fn test_value_serialization_decimal() {
use rust_decimal::Decimal;
let dec = Decimal::new(12345, 2); let value = Value::Decimal(dec);
let json = serde_json::to_value(&value).unwrap();
assert_eq!(json.as_str(), Some("123.45"));
}
#[test]
fn test_value_serialization_datetime() {
use chrono::NaiveDate;
let dt = NaiveDate::from_ymd_opt(2026, 2, 18)
.unwrap()
.and_hms_opt(10, 30, 45)
.unwrap();
let value = Value::DateTime(dt);
let json = serde_json::to_value(&value).unwrap();
let s = json.as_str().unwrap();
assert!(s.starts_with("2026-02-18T10:30:45"));
}
#[test]
fn test_value_serialization_uuid() {
use uuid::Uuid;
let id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
let value = Value::Uuid(id);
let json = serde_json::to_value(&value).unwrap();
assert_eq!(json.as_str(), Some("550e8400-e29b-41d4-a716-446655440000"));
}
#[test]
fn test_value_serialization_bytes() {
let value = Value::Bytes(vec![72, 101, 108, 108, 111]);
let json = serde_json::to_value(&value).unwrap();
assert_eq!(json.as_str(), Some("SGVsbG8="));
}
#[test]
fn test_value_serialization_json() {
use serde_json::json;
let j = json!({"name": "Alice", "age": 30});
let value = Value::Json(j.clone());
let serialized = serde_json::to_value(&value).unwrap();
assert_eq!(serialized, j);
let deserialized: Value = serde_json::from_value(serialized).unwrap();
assert_eq!(deserialized, Value::Json(j));
}
#[test]
fn test_value_serialization_array() {
let value = Value::Array(vec![
Value::String("a".to_string()),
Value::String("b".to_string()),
Value::String("c".to_string()),
]);
let json = serde_json::to_value(&value).unwrap();
assert_eq!(json[0].as_str(), Some("a"));
assert_eq!(json[1].as_str(), Some("b"));
assert_eq!(json[2].as_str(), Some("c"));
let deserialized: Value = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, value);
}
#[test]
fn test_value_serialization_array2d() {
let value = Value::Array2D(vec![
vec![Value::I32(1), Value::I32(2)],
vec![Value::I32(3), Value::I32(4)],
]);
let json = serde_json::to_value(&value).unwrap();
assert_eq!(json[0][0].as_i64(), Some(1));
assert_eq!(json[0][1].as_i64(), Some(2));
assert_eq!(json[1][0].as_i64(), Some(3));
assert_eq!(json[1][1].as_i64(), Some(4));
let expected = Value::Array(vec![
Value::Array(vec![Value::I32(1), Value::I32(2)]),
Value::Array(vec![Value::I32(3), Value::I32(4)]),
]);
let deserialized: Value = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, expected);
}
#[test]
fn test_value_round_trip() {
let values = vec![
Value::Null,
Value::Bool(false),
Value::I32(-42),
Value::I64(9007199254740991), Value::F64(f64::consts::E),
Value::String("test".to_string()),
Value::Array(vec![Value::I32(1), Value::I32(2)]),
];
for value in values {
let json = serde_json::to_value(&value).unwrap();
let deserialized: Value = serde_json::from_value(json).unwrap();
assert_eq!(deserialized, value);
}
}
}