use crate::value::Value;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CastType {
Integer,
Float,
Boolean,
String,
Json,
DateTime,
Date,
Time,
Bytes,
Array,
}
impl CastType {
pub fn name(&self) -> &'static str {
match self {
CastType::Integer => "integer",
CastType::Float => "float",
CastType::Boolean => "boolean",
CastType::String => "string",
CastType::Json => "json",
CastType::DateTime => "datetime",
CastType::Date => "date",
CastType::Time => "time",
CastType::Bytes => "bytes",
CastType::Array => "array",
}
}
}
pub trait Accessor: Send + Sync {
fn field(&self) -> &str;
fn read(&self, value: Value) -> Value;
}
pub trait Mutator: Send + Sync {
fn field(&self) -> &str;
fn write(&self, value: Value) -> Value;
}
pub struct ClosureAccessor {
pub field_name: String,
pub reader: Box<dyn Fn(Value) -> Value + Send + Sync>,
}
impl ClosureAccessor {
pub fn new(
field: impl Into<String>,
reader: impl Fn(Value) -> Value + Send + Sync + 'static,
) -> Self {
Self {
field_name: field.into(),
reader: Box::new(reader),
}
}
}
impl Accessor for ClosureAccessor {
fn field(&self) -> &str {
&self.field_name
}
fn read(&self, value: Value) -> Value {
(self.reader)(value)
}
}
pub struct ClosureMutator {
pub field_name: String,
pub writer: Box<dyn Fn(Value) -> Value + Send + Sync>,
}
impl ClosureMutator {
pub fn new(
field: impl Into<String>,
writer: impl Fn(Value) -> Value + Send + Sync + 'static,
) -> Self {
Self {
field_name: field.into(),
writer: Box::new(writer),
}
}
}
impl Mutator for ClosureMutator {
fn field(&self) -> &str {
&self.field_name
}
fn write(&self, value: Value) -> Value {
(self.writer)(value)
}
}
pub struct AttributeCaster;
impl AttributeCaster {
pub fn cast_read(value: Value, target: CastType) -> Value {
match target {
CastType::Integer => Self::to_integer(value),
CastType::Float => Self::to_float(value),
CastType::Boolean => Self::to_boolean(value),
CastType::String => Self::to_string_value(value),
CastType::Json => Self::to_json(value),
CastType::DateTime => Self::to_datetime(value),
CastType::Date => Self::to_date(value),
CastType::Time => Self::to_time(value),
CastType::Bytes => Self::to_bytes(value),
CastType::Array => Self::to_array(value),
}
}
pub fn cast_write(value: Value, target: CastType) -> Value {
match target {
CastType::Integer => Self::to_integer(value),
CastType::Float => Self::to_float(value),
CastType::Boolean => Self::to_boolean_storage(value),
CastType::String => Self::to_string_value(value),
CastType::Json => Self::to_json_storage(value),
CastType::DateTime => Self::to_datetime_storage(value),
CastType::Date => Self::to_date_storage(value),
CastType::Time => Self::to_time_storage(value),
CastType::Bytes => Self::to_bytes(value),
CastType::Array => Self::to_array_storage(value),
}
}
fn to_integer(value: Value) -> Value {
match value {
Value::I64(_) | Value::I32(_) | Value::I8(_) | Value::I16(_) => value,
Value::U32(v) => Value::I64(v as i64),
Value::U64(v) => Value::I64(v as i64),
Value::U8(v) => Value::I64(v as i64),
Value::U16(v) => Value::I64(v as i64),
Value::F32(v) => Value::I64(v as i64),
Value::F64(v) => Value::I64(v as i64),
Value::Bool(b) => Value::I64(if b { 1 } else { 0 }),
Value::String(s) => {
if let Ok(n) = s.trim().parse::<i64>() {
Value::I64(n)
} else {
Value::Null
}
}
Value::Null => Value::Null,
_ => Value::Null,
}
}
fn to_float(value: Value) -> Value {
match value {
Value::F32(_) | Value::F64(_) => value,
Value::I64(v) => Value::F64(v as f64),
Value::I32(v) => Value::F64(v as f64),
Value::I8(v) => Value::F64(v as f64),
Value::I16(v) => Value::F64(v as f64),
Value::U32(v) => Value::F64(v as f64),
Value::U64(v) => Value::F64(v as f64),
Value::U8(v) => Value::F64(v as f64),
Value::U16(v) => Value::F64(v as f64),
Value::Bool(b) => Value::F64(if b { 1.0 } else { 0.0 }),
Value::String(s) => {
if let Ok(n) = s.trim().parse::<f64>() {
Value::F64(n)
} else {
Value::Null
}
}
Value::Null => Value::Null,
_ => Value::Null,
}
}
fn to_boolean(value: Value) -> Value {
match value {
Value::Bool(_) => value,
Value::I64(v) => Value::Bool(v != 0),
Value::I32(v) => Value::Bool(v != 0),
Value::I8(v) => Value::Bool(v != 0),
Value::I16(v) => Value::Bool(v != 0),
Value::U32(v) => Value::Bool(v != 0),
Value::U64(v) => Value::Bool(v != 0),
Value::U8(v) => Value::Bool(v != 0),
Value::U16(v) => Value::Bool(v != 0),
Value::F32(v) => Value::Bool(v != 0.0),
Value::F64(v) => Value::Bool(v != 0.0),
Value::String(s) => {
let lower = s.trim().to_lowercase();
Value::Bool(matches!(
lower.as_str(),
"1" | "true" | "yes" | "on" | "y" | "t"
))
}
Value::Null => Value::Null,
_ => Value::Null,
}
}
fn to_boolean_storage(value: Value) -> Value {
match value {
Value::Bool(b) => Value::I64(if b { 1 } else { 0 }),
Value::I64(_) | Value::I32(_) | Value::I8(_) | Value::I16(_) => value,
Value::U32(v) => Value::I64(if v != 0 { 1 } else { 0 }),
Value::U64(v) => Value::I64(if v != 0 { 1 } else { 0 }),
Value::U8(v) => Value::I64(if v != 0 { 1 } else { 0 }),
Value::U16(v) => Value::I64(if v != 0 { 1 } else { 0 }),
Value::F32(v) => Value::I64(if v != 0.0 { 1 } else { 0 }),
Value::F64(v) => Value::I64(if v != 0.0 { 1 } else { 0 }),
Value::String(s) => {
let lower = s.trim().to_lowercase();
Value::I64(
if matches!(lower.as_str(), "1" | "true" | "yes" | "on" | "y" | "t") {
1
} else {
0
},
)
}
Value::Null => Value::Null,
_ => Value::Null,
}
}
fn to_string_value(value: Value) -> Value {
match value {
Value::String(_) => value,
Value::I64(v) => Value::String(v.to_string()),
Value::I32(v) => Value::String(v.to_string()),
Value::I8(v) => Value::String(v.to_string()),
Value::I16(v) => Value::String(v.to_string()),
Value::U32(v) => Value::String(v.to_string()),
Value::U64(v) => Value::String(v.to_string()),
Value::U8(v) => Value::String(v.to_string()),
Value::U16(v) => Value::String(v.to_string()),
Value::F32(v) => Value::String(v.to_string()),
Value::F64(v) => Value::String(v.to_string()),
Value::Bool(b) => Value::String(b.to_string()),
Value::Null => Value::Null,
other => Value::String(format!("{:?}", other)),
}
}
fn to_json(value: Value) -> Value {
match value {
Value::String(s) => {
Value::String(s)
}
Value::Json(s) => Value::Json(s),
other => Value::Json(format!("{:?}", other)),
}
}
fn to_json_storage(value: Value) -> Value {
match value {
Value::Json(s) => Value::Json(s),
Value::String(s) => Value::Json(s),
other => Value::Json(format!("{:?}", other)),
}
}
fn to_datetime(value: Value) -> Value {
match value {
Value::DateTime(s) => Value::DateTime(s),
Value::String(s) => Value::DateTime(s),
Value::Null => Value::Null,
other => Value::DateTime(format!("{:?}", other)),
}
}
fn to_datetime_storage(value: Value) -> Value {
match value {
Value::DateTime(s) => Value::DateTime(s),
Value::String(s) => Value::DateTime(s),
Value::Null => Value::Null,
other => Value::DateTime(format!("{:?}", other)),
}
}
fn to_date(value: Value) -> Value {
match value {
Value::Date(s) => Value::Date(s),
Value::String(s) => Value::Date(s),
Value::Null => Value::Null,
other => Value::Date(format!("{:?}", other)),
}
}
fn to_date_storage(value: Value) -> Value {
match value {
Value::Date(s) => Value::Date(s),
Value::String(s) => Value::Date(s),
Value::Null => Value::Null,
other => Value::Date(format!("{:?}", other)),
}
}
fn to_time(value: Value) -> Value {
match value {
Value::Time(s) => Value::Time(s),
Value::String(s) => Value::Time(s),
Value::Null => Value::Null,
other => Value::Time(format!("{:?}", other)),
}
}
fn to_time_storage(value: Value) -> Value {
match value {
Value::Time(s) => Value::Time(s),
Value::String(s) => Value::Time(s),
Value::Null => Value::Null,
other => Value::Time(format!("{:?}", other)),
}
}
fn to_bytes(value: Value) -> Value {
match value {
Value::Bytes(_) => value,
Value::String(s) => Value::Bytes(s.into_bytes()),
Value::Null => Value::Null,
_ => Value::Null,
}
}
fn to_array(value: Value) -> Value {
match value {
Value::Array(_) => value,
Value::Json(s) => {
Value::Array(vec![Value::Json(s)])
}
Value::String(s) => Value::Array(vec![Value::String(s)]),
Value::Null => Value::Null,
other => Value::Array(vec![other]),
}
}
fn to_array_storage(value: Value) -> Value {
match value {
Value::Array(items) => {
Value::Json(format!("{:?}", items))
}
other => Value::Json(format!("{:?}", other)),
}
}
}
pub struct AccessorRegistry {
accessors: HashMap<String, Box<dyn Accessor>>,
mutators: HashMap<String, Box<dyn Mutator>>,
casts: HashMap<String, CastType>,
}
impl Default for AccessorRegistry {
fn default() -> Self {
Self::new()
}
}
impl AccessorRegistry {
pub fn new() -> Self {
Self {
accessors: HashMap::new(),
mutators: HashMap::new(),
casts: HashMap::new(),
}
}
pub fn register_accessor(&mut self, accessor: Box<dyn Accessor>) {
let field = accessor.field().to_string();
self.accessors.insert(field, accessor);
}
pub fn register_mutator(&mut self, mutator: Box<dyn Mutator>) {
let field = mutator.field().to_string();
self.mutators.insert(field, mutator);
}
pub fn register_cast(&mut self, field: impl Into<String>, cast: CastType) {
self.casts.insert(field.into(), cast);
}
pub fn read(&self, field: &str, value: Value) -> Value {
let v1 = if let Some(cast) = self.casts.get(field) {
AttributeCaster::cast_read(value, *cast)
} else {
value
};
if let Some(accessor) = self.accessors.get(field) {
accessor.read(v1)
} else {
v1
}
}
pub fn write(&self, field: &str, value: Value) -> Value {
let v1 = if let Some(mutator) = self.mutators.get(field) {
mutator.write(value)
} else {
value
};
if let Some(cast) = self.casts.get(field) {
AttributeCaster::cast_write(v1, *cast)
} else {
v1
}
}
pub fn cast_read(&self, field: &str, value: Value) -> Value {
if let Some(cast) = self.casts.get(field) {
AttributeCaster::cast_read(value, *cast)
} else {
value
}
}
pub fn cast_write(&self, field: &str, value: Value) -> Value {
if let Some(cast) = self.casts.get(field) {
AttributeCaster::cast_write(value, *cast)
} else {
value
}
}
pub fn has_accessor(&self, field: &str) -> bool {
self.accessors.contains_key(field)
}
pub fn has_mutator(&self, field: &str) -> bool {
self.mutators.contains_key(field)
}
pub fn has_cast(&self, field: &str) -> bool {
self.casts.contains_key(field)
}
pub fn get_cast(&self, field: &str) -> Option<CastType> {
self.casts.get(field).copied()
}
pub fn accessor_count(&self) -> usize {
self.accessors.len()
}
pub fn mutator_count(&self) -> usize {
self.mutators.len()
}
pub fn cast_count(&self) -> usize {
self.casts.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cast_type_name() {
assert_eq!(CastType::Integer.name(), "integer");
assert_eq!(CastType::Boolean.name(), "boolean");
assert_eq!(CastType::Json.name(), "json");
assert_eq!(CastType::DateTime.name(), "datetime");
}
#[test]
fn test_cast_to_integer_from_string() {
let v = AttributeCaster::cast_read(Value::String("42".to_string()), CastType::Integer);
assert_eq!(v, Value::I64(42));
}
#[test]
fn test_cast_to_integer_from_invalid_string() {
let v = AttributeCaster::cast_read(Value::String("abc".to_string()), CastType::Integer);
assert_eq!(v, Value::Null);
}
#[test]
fn test_cast_to_integer_from_bool() {
let v = AttributeCaster::cast_read(Value::Bool(true), CastType::Integer);
assert_eq!(v, Value::I64(1));
}
#[test]
fn test_cast_to_integer_from_float() {
let v = AttributeCaster::cast_read(Value::F64(3.7), CastType::Integer);
assert_eq!(v, Value::I64(3));
}
#[test]
fn test_cast_to_integer_preserves_i64() {
let v = AttributeCaster::cast_read(Value::I64(100), CastType::Integer);
assert_eq!(v, Value::I64(100));
}
#[test]
fn test_cast_to_float_from_string() {
let v = AttributeCaster::cast_read(Value::String("3.15".to_string()), CastType::Float);
assert_eq!(v, Value::F64(3.15));
}
#[test]
fn test_cast_to_float_from_i64() {
let v = AttributeCaster::cast_read(Value::I64(42), CastType::Float);
assert_eq!(v, Value::F64(42.0));
}
#[test]
fn test_cast_to_boolean_from_i64_one() {
let v = AttributeCaster::cast_read(Value::I64(1), CastType::Boolean);
assert_eq!(v, Value::Bool(true));
}
#[test]
fn test_cast_to_boolean_from_i64_zero() {
let v = AttributeCaster::cast_read(Value::I64(0), CastType::Boolean);
assert_eq!(v, Value::Bool(false));
}
#[test]
fn test_cast_to_boolean_from_string_true() {
let v = AttributeCaster::cast_read(Value::String("true".to_string()), CastType::Boolean);
assert_eq!(v, Value::Bool(true));
}
#[test]
fn test_cast_to_boolean_from_string_yes() {
let v = AttributeCaster::cast_read(Value::String("yes".to_string()), CastType::Boolean);
assert_eq!(v, Value::Bool(true));
}
#[test]
fn test_cast_to_boolean_from_string_on() {
let v = AttributeCaster::cast_read(Value::String("on".to_string()), CastType::Boolean);
assert_eq!(v, Value::Bool(true));
}
#[test]
fn test_cast_to_boolean_from_string_random() {
let v = AttributeCaster::cast_read(Value::String("random".to_string()), CastType::Boolean);
assert_eq!(v, Value::Bool(false));
}
#[test]
fn test_cast_to_boolean_preserves_bool() {
let v = AttributeCaster::cast_read(Value::Bool(true), CastType::Boolean);
assert_eq!(v, Value::Bool(true));
}
#[test]
fn test_cast_to_boolean_storage_from_bool() {
let v = AttributeCaster::cast_write(Value::Bool(true), CastType::Boolean);
assert_eq!(v, Value::I64(1));
}
#[test]
fn test_cast_to_boolean_storage_from_string() {
let v = AttributeCaster::cast_write(Value::String("yes".to_string()), CastType::Boolean);
assert_eq!(v, Value::I64(1));
}
#[test]
fn test_cast_to_string_from_i64() {
let v = AttributeCaster::cast_read(Value::I64(42), CastType::String);
assert_eq!(v, Value::String("42".to_string()));
}
#[test]
fn test_cast_to_string_from_bool() {
let v = AttributeCaster::cast_read(Value::Bool(true), CastType::String);
assert_eq!(v, Value::String("true".to_string()));
}
#[test]
fn test_cast_to_string_preserves_string() {
let v = AttributeCaster::cast_read(Value::String("hello".to_string()), CastType::String);
assert_eq!(v, Value::String("hello".to_string()));
}
#[test]
fn test_cast_to_json_from_string() {
let v = AttributeCaster::cast_read(
Value::String(r#"{"key":"value"}"#.to_string()),
CastType::Json,
);
assert!(matches!(v, Value::String(_)));
}
#[test]
fn test_cast_to_json_from_other() {
let v = AttributeCaster::cast_read(Value::I64(42), CastType::Json);
assert!(matches!(v, Value::Json(_)));
}
#[test]
fn test_cast_to_datetime_from_string() {
let v = AttributeCaster::cast_read(
Value::String("2026-07-19T10:00:00Z".to_string()),
CastType::DateTime,
);
assert_eq!(v, Value::DateTime("2026-07-19T10:00:00Z".to_string()));
}
#[test]
fn test_cast_to_date_from_string() {
let v = AttributeCaster::cast_read(Value::String("2026-07-19".to_string()), CastType::Date);
assert_eq!(v, Value::Date("2026-07-19".to_string()));
}
#[test]
fn test_cast_to_time_from_string() {
let v = AttributeCaster::cast_read(Value::String("10:30:00".to_string()), CastType::Time);
assert_eq!(v, Value::Time("10:30:00".to_string()));
}
#[test]
fn test_cast_to_bytes_from_string() {
let v = AttributeCaster::cast_read(Value::String("hello".to_string()), CastType::Bytes);
assert_eq!(v, Value::Bytes(b"hello".to_vec()));
}
#[test]
fn test_cast_to_bytes_preserves_bytes() {
let v = AttributeCaster::cast_read(Value::Bytes(b"data".to_vec()), CastType::Bytes);
assert_eq!(v, Value::Bytes(b"data".to_vec()));
}
#[test]
fn test_cast_to_array_from_string() {
let v = AttributeCaster::cast_read(Value::String("item".to_string()), CastType::Array);
assert!(matches!(v, Value::Array(_)));
if let Value::Array(arr) = v {
assert_eq!(arr.len(), 1);
}
}
#[test]
fn test_cast_to_array_preserves_array() {
let arr = vec![Value::I64(1), Value::I64(2)];
let v = AttributeCaster::cast_read(Value::Array(arr.clone()), CastType::Array);
assert_eq!(v, Value::Array(arr));
}
#[test]
fn test_cast_to_array_storage_serializes_to_json() {
let v = AttributeCaster::cast_write(
Value::Array(vec![Value::I64(1), Value::I64(2)]),
CastType::Array,
);
assert!(matches!(v, Value::Json(_)));
}
#[test]
fn test_closure_accessor() {
let accessor = ClosureAccessor::new("name", |v| match v {
Value::String(s) => Value::String(s.to_uppercase()),
other => other,
});
let v = accessor.read(Value::String("alice".to_string()));
assert_eq!(v, Value::String("ALICE".to_string()));
assert_eq!(accessor.field(), "name");
}
#[test]
fn test_closure_mutator() {
let mutator = ClosureMutator::new("email", |v| match v {
Value::String(s) => Value::String(s.to_lowercase()),
other => other,
});
let v = mutator.write(Value::String("ALICE@EXAMPLE.COM".to_string()));
assert_eq!(v, Value::String("alice@example.com".to_string()));
assert_eq!(mutator.field(), "email");
}
#[test]
fn test_registry_empty() {
let r = AccessorRegistry::new();
assert_eq!(r.accessor_count(), 0);
assert_eq!(r.mutator_count(), 0);
assert_eq!(r.cast_count(), 0);
}
#[test]
fn test_registry_register_cast() {
let mut r = AccessorRegistry::new();
r.register_cast("is_admin", CastType::Boolean);
assert!(r.has_cast("is_admin"));
assert_eq!(r.get_cast("is_admin"), Some(CastType::Boolean));
assert_eq!(r.cast_count(), 1);
}
#[test]
fn test_registry_register_accessor() {
let mut r = AccessorRegistry::new();
r.register_accessor(Box::new(ClosureAccessor::new("name", |v| match v {
Value::String(s) => Value::String(s.to_uppercase()),
other => other,
})));
assert!(r.has_accessor("name"));
assert_eq!(r.accessor_count(), 1);
}
#[test]
fn test_registry_register_mutator() {
let mut r = AccessorRegistry::new();
r.register_mutator(Box::new(ClosureMutator::new("email", |v| match v {
Value::String(s) => Value::String(s.to_lowercase()),
other => other,
})));
assert!(r.has_mutator("email"));
assert_eq!(r.mutator_count(), 1);
}
#[test]
fn test_registry_read_applies_cast_then_accessor() {
let mut r = AccessorRegistry::new();
r.register_cast("is_admin", CastType::Boolean);
r.register_accessor(Box::new(ClosureAccessor::new("is_admin", |v| {
if v == Value::Bool(true) {
Value::String("管理员".to_string())
} else {
Value::String("普通用户".to_string())
}
})));
let v = r.read("is_admin", Value::I64(1));
assert_eq!(v, Value::String("管理员".to_string()));
}
#[test]
fn test_registry_write_applies_mutator_then_cast() {
let mut r = AccessorRegistry::new();
r.register_cast("is_admin", CastType::Boolean);
r.register_mutator(Box::new(ClosureMutator::new("is_admin", |v| match v {
Value::String(s) => {
let lower = s.to_lowercase();
Value::Bool(lower == "admin" || lower == "true")
}
other => other,
})));
let v = r.write("is_admin", Value::String("admin".to_string()));
assert_eq!(v, Value::I64(1));
}
#[test]
fn test_registry_read_without_cast_or_accessor() {
let r = AccessorRegistry::new();
let v = r.read("any_field", Value::I64(42));
assert_eq!(v, Value::I64(42));
}
#[test]
fn test_registry_write_without_cast_or_mutator() {
let r = AccessorRegistry::new();
let v = r.write("any_field", Value::I64(42));
assert_eq!(v, Value::I64(42));
}
#[test]
fn test_registry_cast_read_only() {
let mut r = AccessorRegistry::new();
r.register_cast("is_admin", CastType::Boolean);
let v = r.cast_read("is_admin", Value::I64(1));
assert_eq!(v, Value::Bool(true));
}
#[test]
fn test_registry_cast_write_only() {
let mut r = AccessorRegistry::new();
r.register_cast("is_admin", CastType::Boolean);
let v = r.cast_write("is_admin", Value::Bool(true));
assert_eq!(v, Value::I64(1));
}
#[test]
fn test_complex_user_model_scenario() {
let mut r = AccessorRegistry::new();
r.register_cast("is_admin", CastType::Boolean);
r.register_mutator(Box::new(ClosureMutator::new("email", |v| match v {
Value::String(s) => Value::String(s.to_lowercase()),
other => other,
})));
r.register_accessor(Box::new(ClosureAccessor::new(
"full_name",
|v| v, )));
r.register_cast("settings", CastType::Json);
r.register_cast("created_at", CastType::DateTime);
let v = r.read("is_admin", Value::I64(1));
assert_eq!(v, Value::Bool(true));
let v = r.write("email", Value::String("Alice@Example.COM".to_string()));
assert_eq!(v, Value::String("alice@example.com".to_string()));
let v = r.read("settings", Value::String(r#"{"theme":"dark"}"#.to_string()));
assert!(matches!(v, Value::String(_)));
assert_eq!(r.accessor_count(), 1);
assert_eq!(r.mutator_count(), 1);
assert_eq!(r.cast_count(), 3);
}
#[test]
fn test_default_is_empty() {
let r = AccessorRegistry::default();
assert_eq!(r.accessor_count(), 0);
assert_eq!(r.mutator_count(), 0);
assert_eq!(r.cast_count(), 0);
}
}