1use crate::value::Value;
41use std::collections::HashMap;
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51pub enum CastType {
52 Integer,
54 Float,
56 Boolean,
58 String,
60 Json,
62 DateTime,
64 Date,
66 Time,
68 Bytes,
70 Array,
72}
73
74impl CastType {
75 pub fn name(&self) -> &'static str {
77 match self {
78 CastType::Integer => "integer",
79 CastType::Float => "float",
80 CastType::Boolean => "boolean",
81 CastType::String => "string",
82 CastType::Json => "json",
83 CastType::DateTime => "datetime",
84 CastType::Date => "date",
85 CastType::Time => "time",
86 CastType::Bytes => "bytes",
87 CastType::Array => "array",
88 }
89 }
90}
91
92pub trait Accessor: Send + Sync {
100 fn field(&self) -> &str;
102
103 fn read(&self, value: Value) -> Value;
105}
106
107pub trait Mutator: Send + Sync {
111 fn field(&self) -> &str;
113
114 fn write(&self, value: Value) -> Value;
116}
117
118pub struct ClosureAccessor {
124 pub field_name: String,
126 pub reader: Box<dyn Fn(Value) -> Value + Send + Sync>,
128}
129
130impl ClosureAccessor {
131 pub fn new(
133 field: impl Into<String>,
134 reader: impl Fn(Value) -> Value + Send + Sync + 'static,
135 ) -> Self {
136 Self {
137 field_name: field.into(),
138 reader: Box::new(reader),
139 }
140 }
141}
142
143impl Accessor for ClosureAccessor {
144 fn field(&self) -> &str {
145 &self.field_name
146 }
147
148 fn read(&self, value: Value) -> Value {
149 (self.reader)(value)
150 }
151}
152
153pub struct ClosureMutator {
155 pub field_name: String,
157 pub writer: Box<dyn Fn(Value) -> Value + Send + Sync>,
159}
160
161impl ClosureMutator {
162 pub fn new(
164 field: impl Into<String>,
165 writer: impl Fn(Value) -> Value + Send + Sync + 'static,
166 ) -> Self {
167 Self {
168 field_name: field.into(),
169 writer: Box::new(writer),
170 }
171 }
172}
173
174impl Mutator for ClosureMutator {
175 fn field(&self) -> &str {
176 &self.field_name
177 }
178
179 fn write(&self, value: Value) -> Value {
180 (self.writer)(value)
181 }
182}
183
184pub struct AttributeCaster;
192
193impl AttributeCaster {
194 pub fn cast_read(value: Value, target: CastType) -> Value {
196 match target {
197 CastType::Integer => Self::to_integer(value),
198 CastType::Float => Self::to_float(value),
199 CastType::Boolean => Self::to_boolean(value),
200 CastType::String => Self::to_string_value(value),
201 CastType::Json => Self::to_json(value),
202 CastType::DateTime => Self::to_datetime(value),
203 CastType::Date => Self::to_date(value),
204 CastType::Time => Self::to_time(value),
205 CastType::Bytes => Self::to_bytes(value),
206 CastType::Array => Self::to_array(value),
207 }
208 }
209
210 pub fn cast_write(value: Value, target: CastType) -> Value {
212 match target {
213 CastType::Integer => Self::to_integer(value),
214 CastType::Float => Self::to_float(value),
215 CastType::Boolean => Self::to_boolean_storage(value),
216 CastType::String => Self::to_string_value(value),
217 CastType::Json => Self::to_json_storage(value),
218 CastType::DateTime => Self::to_datetime_storage(value),
219 CastType::Date => Self::to_date_storage(value),
220 CastType::Time => Self::to_time_storage(value),
221 CastType::Bytes => Self::to_bytes(value),
222 CastType::Array => Self::to_array_storage(value),
223 }
224 }
225
226 fn to_integer(value: Value) -> Value {
229 match value {
230 Value::I64(_) | Value::I32(_) | Value::I8(_) | Value::I16(_) => value,
231 Value::U32(v) => Value::I64(v as i64),
232 Value::U64(v) => Value::I64(v as i64),
233 Value::U8(v) => Value::I64(v as i64),
234 Value::U16(v) => Value::I64(v as i64),
235 Value::F32(v) => Value::I64(v as i64),
236 Value::F64(v) => Value::I64(v as i64),
237 Value::Bool(b) => Value::I64(if b { 1 } else { 0 }),
238 Value::String(s) => {
239 if let Ok(n) = s.trim().parse::<i64>() {
240 Value::I64(n)
241 } else {
242 Value::Null
243 }
244 }
245 Value::Null => Value::Null,
246 _ => Value::Null,
247 }
248 }
249
250 fn to_float(value: Value) -> Value {
251 match value {
252 Value::F32(_) | Value::F64(_) => value,
253 Value::I64(v) => Value::F64(v as f64),
254 Value::I32(v) => Value::F64(v as f64),
255 Value::I8(v) => Value::F64(v as f64),
256 Value::I16(v) => Value::F64(v as f64),
257 Value::U32(v) => Value::F64(v as f64),
258 Value::U64(v) => Value::F64(v as f64),
259 Value::U8(v) => Value::F64(v as f64),
260 Value::U16(v) => Value::F64(v as f64),
261 Value::Bool(b) => Value::F64(if b { 1.0 } else { 0.0 }),
262 Value::String(s) => {
263 if let Ok(n) = s.trim().parse::<f64>() {
264 Value::F64(n)
265 } else {
266 Value::Null
267 }
268 }
269 Value::Null => Value::Null,
270 _ => Value::Null,
271 }
272 }
273
274 fn to_boolean(value: Value) -> Value {
275 match value {
276 Value::Bool(_) => value,
277 Value::I64(v) => Value::Bool(v != 0),
278 Value::I32(v) => Value::Bool(v != 0),
279 Value::I8(v) => Value::Bool(v != 0),
280 Value::I16(v) => Value::Bool(v != 0),
281 Value::U32(v) => Value::Bool(v != 0),
282 Value::U64(v) => Value::Bool(v != 0),
283 Value::U8(v) => Value::Bool(v != 0),
284 Value::U16(v) => Value::Bool(v != 0),
285 Value::F32(v) => Value::Bool(v != 0.0),
286 Value::F64(v) => Value::Bool(v != 0.0),
287 Value::String(s) => {
288 let lower = s.trim().to_lowercase();
289 Value::Bool(matches!(
290 lower.as_str(),
291 "1" | "true" | "yes" | "on" | "y" | "t"
292 ))
293 }
294 Value::Null => Value::Null,
295 _ => Value::Null,
296 }
297 }
298
299 fn to_boolean_storage(value: Value) -> Value {
300 match value {
301 Value::Bool(b) => Value::I64(if b { 1 } else { 0 }),
302 Value::I64(_) | Value::I32(_) | Value::I8(_) | Value::I16(_) => value,
303 Value::U32(v) => Value::I64(if v != 0 { 1 } else { 0 }),
304 Value::U64(v) => Value::I64(if v != 0 { 1 } else { 0 }),
305 Value::U8(v) => Value::I64(if v != 0 { 1 } else { 0 }),
306 Value::U16(v) => Value::I64(if v != 0 { 1 } else { 0 }),
307 Value::F32(v) => Value::I64(if v != 0.0 { 1 } else { 0 }),
308 Value::F64(v) => Value::I64(if v != 0.0 { 1 } else { 0 }),
309 Value::String(s) => {
310 let lower = s.trim().to_lowercase();
311 Value::I64(
312 if matches!(lower.as_str(), "1" | "true" | "yes" | "on" | "y" | "t") {
313 1
314 } else {
315 0
316 },
317 )
318 }
319 Value::Null => Value::Null,
320 _ => Value::Null,
321 }
322 }
323
324 fn to_string_value(value: Value) -> Value {
325 match value {
326 Value::String(_) => value,
327 Value::I64(v) => Value::String(v.to_string()),
328 Value::I32(v) => Value::String(v.to_string()),
329 Value::I8(v) => Value::String(v.to_string()),
330 Value::I16(v) => Value::String(v.to_string()),
331 Value::U32(v) => Value::String(v.to_string()),
332 Value::U64(v) => Value::String(v.to_string()),
333 Value::U8(v) => Value::String(v.to_string()),
334 Value::U16(v) => Value::String(v.to_string()),
335 Value::F32(v) => Value::String(v.to_string()),
336 Value::F64(v) => Value::String(v.to_string()),
337 Value::Bool(b) => Value::String(b.to_string()),
338 Value::Null => Value::Null,
339 other => Value::String(format!("{:?}", other)),
340 }
341 }
342
343 fn to_json(value: Value) -> Value {
344 match value {
345 Value::String(s) => {
346 Value::String(s)
349 }
350 Value::Json(s) => Value::Json(s),
351 other => Value::Json(format!("{:?}", other)),
352 }
353 }
354
355 fn to_json_storage(value: Value) -> Value {
356 match value {
357 Value::Json(s) => Value::Json(s),
358 Value::String(s) => Value::Json(s),
359 other => Value::Json(format!("{:?}", other)),
360 }
361 }
362
363 fn to_datetime(value: Value) -> Value {
364 match value {
365 Value::DateTime(s) => Value::DateTime(s),
366 Value::String(s) => Value::DateTime(s),
367 Value::Null => Value::Null,
368 other => Value::DateTime(format!("{:?}", other)),
369 }
370 }
371
372 fn to_datetime_storage(value: Value) -> Value {
373 match value {
374 Value::DateTime(s) => Value::DateTime(s),
375 Value::String(s) => Value::DateTime(s),
376 Value::Null => Value::Null,
377 other => Value::DateTime(format!("{:?}", other)),
378 }
379 }
380
381 fn to_date(value: Value) -> Value {
382 match value {
383 Value::Date(s) => Value::Date(s),
384 Value::String(s) => Value::Date(s),
385 Value::Null => Value::Null,
386 other => Value::Date(format!("{:?}", other)),
387 }
388 }
389
390 fn to_date_storage(value: Value) -> Value {
391 match value {
392 Value::Date(s) => Value::Date(s),
393 Value::String(s) => Value::Date(s),
394 Value::Null => Value::Null,
395 other => Value::Date(format!("{:?}", other)),
396 }
397 }
398
399 fn to_time(value: Value) -> Value {
400 match value {
401 Value::Time(s) => Value::Time(s),
402 Value::String(s) => Value::Time(s),
403 Value::Null => Value::Null,
404 other => Value::Time(format!("{:?}", other)),
405 }
406 }
407
408 fn to_time_storage(value: Value) -> Value {
409 match value {
410 Value::Time(s) => Value::Time(s),
411 Value::String(s) => Value::Time(s),
412 Value::Null => Value::Null,
413 other => Value::Time(format!("{:?}", other)),
414 }
415 }
416
417 fn to_bytes(value: Value) -> Value {
418 match value {
419 Value::Bytes(_) => value,
420 Value::String(s) => Value::Bytes(s.into_bytes()),
421 Value::Null => Value::Null,
422 _ => Value::Null,
423 }
424 }
425
426 fn to_array(value: Value) -> Value {
427 match value {
428 Value::Array(_) => value,
429 Value::Json(s) => {
430 Value::Array(vec![Value::Json(s)])
432 }
433 Value::String(s) => Value::Array(vec![Value::String(s)]),
434 Value::Null => Value::Null,
435 other => Value::Array(vec![other]),
436 }
437 }
438
439 fn to_array_storage(value: Value) -> Value {
440 match value {
441 Value::Array(items) => {
442 Value::Json(format!("{:?}", items))
444 }
445 other => Value::Json(format!("{:?}", other)),
446 }
447 }
448}
449
450pub struct AccessorRegistry {
458 accessors: HashMap<String, Box<dyn Accessor>>,
460 mutators: HashMap<String, Box<dyn Mutator>>,
462 casts: HashMap<String, CastType>,
464}
465
466impl Default for AccessorRegistry {
467 fn default() -> Self {
468 Self::new()
469 }
470}
471
472impl AccessorRegistry {
473 pub fn new() -> Self {
475 Self {
476 accessors: HashMap::new(),
477 mutators: HashMap::new(),
478 casts: HashMap::new(),
479 }
480 }
481
482 pub fn register_accessor(&mut self, accessor: Box<dyn Accessor>) {
484 let field = accessor.field().to_string();
485 self.accessors.insert(field, accessor);
486 }
487
488 pub fn register_mutator(&mut self, mutator: Box<dyn Mutator>) {
490 let field = mutator.field().to_string();
491 self.mutators.insert(field, mutator);
492 }
493
494 pub fn register_cast(&mut self, field: impl Into<String>, cast: CastType) {
496 self.casts.insert(field.into(), cast);
497 }
498
499 pub fn read(&self, field: &str, value: Value) -> Value {
501 let v1 = if let Some(cast) = self.casts.get(field) {
502 AttributeCaster::cast_read(value, *cast)
503 } else {
504 value
505 };
506 if let Some(accessor) = self.accessors.get(field) {
507 accessor.read(v1)
508 } else {
509 v1
510 }
511 }
512
513 pub fn write(&self, field: &str, value: Value) -> Value {
515 let v1 = if let Some(mutator) = self.mutators.get(field) {
516 mutator.write(value)
517 } else {
518 value
519 };
520 if let Some(cast) = self.casts.get(field) {
521 AttributeCaster::cast_write(v1, *cast)
522 } else {
523 v1
524 }
525 }
526
527 pub fn cast_read(&self, field: &str, value: Value) -> Value {
529 if let Some(cast) = self.casts.get(field) {
530 AttributeCaster::cast_read(value, *cast)
531 } else {
532 value
533 }
534 }
535
536 pub fn cast_write(&self, field: &str, value: Value) -> Value {
538 if let Some(cast) = self.casts.get(field) {
539 AttributeCaster::cast_write(value, *cast)
540 } else {
541 value
542 }
543 }
544
545 pub fn has_accessor(&self, field: &str) -> bool {
547 self.accessors.contains_key(field)
548 }
549
550 pub fn has_mutator(&self, field: &str) -> bool {
552 self.mutators.contains_key(field)
553 }
554
555 pub fn has_cast(&self, field: &str) -> bool {
557 self.casts.contains_key(field)
558 }
559
560 pub fn get_cast(&self, field: &str) -> Option<CastType> {
562 self.casts.get(field).copied()
563 }
564
565 pub fn accessor_count(&self) -> usize {
567 self.accessors.len()
568 }
569
570 pub fn mutator_count(&self) -> usize {
572 self.mutators.len()
573 }
574
575 pub fn cast_count(&self) -> usize {
577 self.casts.len()
578 }
579}
580
581#[cfg(test)]
586mod tests {
587 use super::*;
588
589 #[test]
592 fn test_cast_type_name() {
593 assert_eq!(CastType::Integer.name(), "integer");
594 assert_eq!(CastType::Boolean.name(), "boolean");
595 assert_eq!(CastType::Json.name(), "json");
596 assert_eq!(CastType::DateTime.name(), "datetime");
597 }
598
599 #[test]
602 fn test_cast_to_integer_from_string() {
603 let v = AttributeCaster::cast_read(Value::String("42".to_string()), CastType::Integer);
604 assert_eq!(v, Value::I64(42));
605 }
606
607 #[test]
608 fn test_cast_to_integer_from_invalid_string() {
609 let v = AttributeCaster::cast_read(Value::String("abc".to_string()), CastType::Integer);
610 assert_eq!(v, Value::Null);
611 }
612
613 #[test]
614 fn test_cast_to_integer_from_bool() {
615 let v = AttributeCaster::cast_read(Value::Bool(true), CastType::Integer);
616 assert_eq!(v, Value::I64(1));
617 }
618
619 #[test]
620 fn test_cast_to_integer_from_float() {
621 let v = AttributeCaster::cast_read(Value::F64(3.7), CastType::Integer);
622 assert_eq!(v, Value::I64(3));
623 }
624
625 #[test]
626 fn test_cast_to_integer_preserves_i64() {
627 let v = AttributeCaster::cast_read(Value::I64(100), CastType::Integer);
628 assert_eq!(v, Value::I64(100));
629 }
630
631 #[test]
634 fn test_cast_to_float_from_string() {
635 let v = AttributeCaster::cast_read(Value::String("3.15".to_string()), CastType::Float);
636 assert_eq!(v, Value::F64(3.15));
637 }
638
639 #[test]
640 fn test_cast_to_float_from_i64() {
641 let v = AttributeCaster::cast_read(Value::I64(42), CastType::Float);
642 assert_eq!(v, Value::F64(42.0));
643 }
644
645 #[test]
648 fn test_cast_to_boolean_from_i64_one() {
649 let v = AttributeCaster::cast_read(Value::I64(1), CastType::Boolean);
650 assert_eq!(v, Value::Bool(true));
651 }
652
653 #[test]
654 fn test_cast_to_boolean_from_i64_zero() {
655 let v = AttributeCaster::cast_read(Value::I64(0), CastType::Boolean);
656 assert_eq!(v, Value::Bool(false));
657 }
658
659 #[test]
660 fn test_cast_to_boolean_from_string_true() {
661 let v = AttributeCaster::cast_read(Value::String("true".to_string()), CastType::Boolean);
662 assert_eq!(v, Value::Bool(true));
663 }
664
665 #[test]
666 fn test_cast_to_boolean_from_string_yes() {
667 let v = AttributeCaster::cast_read(Value::String("yes".to_string()), CastType::Boolean);
668 assert_eq!(v, Value::Bool(true));
669 }
670
671 #[test]
672 fn test_cast_to_boolean_from_string_on() {
673 let v = AttributeCaster::cast_read(Value::String("on".to_string()), CastType::Boolean);
674 assert_eq!(v, Value::Bool(true));
675 }
676
677 #[test]
678 fn test_cast_to_boolean_from_string_random() {
679 let v = AttributeCaster::cast_read(Value::String("random".to_string()), CastType::Boolean);
680 assert_eq!(v, Value::Bool(false));
681 }
682
683 #[test]
684 fn test_cast_to_boolean_preserves_bool() {
685 let v = AttributeCaster::cast_read(Value::Bool(true), CastType::Boolean);
686 assert_eq!(v, Value::Bool(true));
687 }
688
689 #[test]
692 fn test_cast_to_boolean_storage_from_bool() {
693 let v = AttributeCaster::cast_write(Value::Bool(true), CastType::Boolean);
694 assert_eq!(v, Value::I64(1));
695 }
696
697 #[test]
698 fn test_cast_to_boolean_storage_from_string() {
699 let v = AttributeCaster::cast_write(Value::String("yes".to_string()), CastType::Boolean);
700 assert_eq!(v, Value::I64(1));
701 }
702
703 #[test]
706 fn test_cast_to_string_from_i64() {
707 let v = AttributeCaster::cast_read(Value::I64(42), CastType::String);
708 assert_eq!(v, Value::String("42".to_string()));
709 }
710
711 #[test]
712 fn test_cast_to_string_from_bool() {
713 let v = AttributeCaster::cast_read(Value::Bool(true), CastType::String);
714 assert_eq!(v, Value::String("true".to_string()));
715 }
716
717 #[test]
718 fn test_cast_to_string_preserves_string() {
719 let v = AttributeCaster::cast_read(Value::String("hello".to_string()), CastType::String);
720 assert_eq!(v, Value::String("hello".to_string()));
721 }
722
723 #[test]
726 fn test_cast_to_json_from_string() {
727 let v = AttributeCaster::cast_read(
728 Value::String(r#"{"key":"value"}"#.to_string()),
729 CastType::Json,
730 );
731 assert!(matches!(v, Value::String(_)));
733 }
734
735 #[test]
736 fn test_cast_to_json_from_other() {
737 let v = AttributeCaster::cast_read(Value::I64(42), CastType::Json);
738 assert!(matches!(v, Value::Json(_)));
739 }
740
741 #[test]
744 fn test_cast_to_datetime_from_string() {
745 let v = AttributeCaster::cast_read(
746 Value::String("2026-07-19T10:00:00Z".to_string()),
747 CastType::DateTime,
748 );
749 assert_eq!(v, Value::DateTime("2026-07-19T10:00:00Z".to_string()));
750 }
751
752 #[test]
753 fn test_cast_to_date_from_string() {
754 let v = AttributeCaster::cast_read(Value::String("2026-07-19".to_string()), CastType::Date);
755 assert_eq!(v, Value::Date("2026-07-19".to_string()));
756 }
757
758 #[test]
759 fn test_cast_to_time_from_string() {
760 let v = AttributeCaster::cast_read(Value::String("10:30:00".to_string()), CastType::Time);
761 assert_eq!(v, Value::Time("10:30:00".to_string()));
762 }
763
764 #[test]
767 fn test_cast_to_bytes_from_string() {
768 let v = AttributeCaster::cast_read(Value::String("hello".to_string()), CastType::Bytes);
769 assert_eq!(v, Value::Bytes(b"hello".to_vec()));
770 }
771
772 #[test]
773 fn test_cast_to_bytes_preserves_bytes() {
774 let v = AttributeCaster::cast_read(Value::Bytes(b"data".to_vec()), CastType::Bytes);
775 assert_eq!(v, Value::Bytes(b"data".to_vec()));
776 }
777
778 #[test]
781 fn test_cast_to_array_from_string() {
782 let v = AttributeCaster::cast_read(Value::String("item".to_string()), CastType::Array);
783 assert!(matches!(v, Value::Array(_)));
784 if let Value::Array(arr) = v {
785 assert_eq!(arr.len(), 1);
786 }
787 }
788
789 #[test]
790 fn test_cast_to_array_preserves_array() {
791 let arr = vec![Value::I64(1), Value::I64(2)];
792 let v = AttributeCaster::cast_read(Value::Array(arr.clone()), CastType::Array);
793 assert_eq!(v, Value::Array(arr));
794 }
795
796 #[test]
797 fn test_cast_to_array_storage_serializes_to_json() {
798 let v = AttributeCaster::cast_write(
799 Value::Array(vec![Value::I64(1), Value::I64(2)]),
800 CastType::Array,
801 );
802 assert!(matches!(v, Value::Json(_)));
803 }
804
805 #[test]
808 fn test_closure_accessor() {
809 let accessor = ClosureAccessor::new("name", |v| match v {
810 Value::String(s) => Value::String(s.to_uppercase()),
811 other => other,
812 });
813 let v = accessor.read(Value::String("alice".to_string()));
814 assert_eq!(v, Value::String("ALICE".to_string()));
815 assert_eq!(accessor.field(), "name");
816 }
817
818 #[test]
819 fn test_closure_mutator() {
820 let mutator = ClosureMutator::new("email", |v| match v {
821 Value::String(s) => Value::String(s.to_lowercase()),
822 other => other,
823 });
824 let v = mutator.write(Value::String("ALICE@EXAMPLE.COM".to_string()));
825 assert_eq!(v, Value::String("alice@example.com".to_string()));
826 assert_eq!(mutator.field(), "email");
827 }
828
829 #[test]
832 fn test_registry_empty() {
833 let r = AccessorRegistry::new();
834 assert_eq!(r.accessor_count(), 0);
835 assert_eq!(r.mutator_count(), 0);
836 assert_eq!(r.cast_count(), 0);
837 }
838
839 #[test]
840 fn test_registry_register_cast() {
841 let mut r = AccessorRegistry::new();
842 r.register_cast("is_admin", CastType::Boolean);
843 assert!(r.has_cast("is_admin"));
844 assert_eq!(r.get_cast("is_admin"), Some(CastType::Boolean));
845 assert_eq!(r.cast_count(), 1);
846 }
847
848 #[test]
849 fn test_registry_register_accessor() {
850 let mut r = AccessorRegistry::new();
851 r.register_accessor(Box::new(ClosureAccessor::new("name", |v| match v {
852 Value::String(s) => Value::String(s.to_uppercase()),
853 other => other,
854 })));
855 assert!(r.has_accessor("name"));
856 assert_eq!(r.accessor_count(), 1);
857 }
858
859 #[test]
860 fn test_registry_register_mutator() {
861 let mut r = AccessorRegistry::new();
862 r.register_mutator(Box::new(ClosureMutator::new("email", |v| match v {
863 Value::String(s) => Value::String(s.to_lowercase()),
864 other => other,
865 })));
866 assert!(r.has_mutator("email"));
867 assert_eq!(r.mutator_count(), 1);
868 }
869
870 #[test]
873 fn test_registry_read_applies_cast_then_accessor() {
874 let mut r = AccessorRegistry::new();
875 r.register_cast("is_admin", CastType::Boolean);
876 r.register_accessor(Box::new(ClosureAccessor::new("is_admin", |v| {
877 if v == Value::Bool(true) {
878 Value::String("管理员".to_string())
879 } else {
880 Value::String("普通用户".to_string())
881 }
882 })));
883
884 let v = r.read("is_admin", Value::I64(1));
886 assert_eq!(v, Value::String("管理员".to_string()));
887 }
888
889 #[test]
890 fn test_registry_write_applies_mutator_then_cast() {
891 let mut r = AccessorRegistry::new();
892 r.register_cast("is_admin", CastType::Boolean);
893 r.register_mutator(Box::new(ClosureMutator::new("is_admin", |v| match v {
894 Value::String(s) => {
895 let lower = s.to_lowercase();
896 Value::Bool(lower == "admin" || lower == "true")
897 }
898 other => other,
899 })));
900
901 let v = r.write("is_admin", Value::String("admin".to_string()));
903 assert_eq!(v, Value::I64(1));
904 }
905
906 #[test]
907 fn test_registry_read_without_cast_or_accessor() {
908 let r = AccessorRegistry::new();
909 let v = r.read("any_field", Value::I64(42));
910 assert_eq!(v, Value::I64(42));
911 }
912
913 #[test]
914 fn test_registry_write_without_cast_or_mutator() {
915 let r = AccessorRegistry::new();
916 let v = r.write("any_field", Value::I64(42));
917 assert_eq!(v, Value::I64(42));
918 }
919
920 #[test]
921 fn test_registry_cast_read_only() {
922 let mut r = AccessorRegistry::new();
923 r.register_cast("is_admin", CastType::Boolean);
924
925 let v = r.cast_read("is_admin", Value::I64(1));
926 assert_eq!(v, Value::Bool(true));
927 }
928
929 #[test]
930 fn test_registry_cast_write_only() {
931 let mut r = AccessorRegistry::new();
932 r.register_cast("is_admin", CastType::Boolean);
933
934 let v = r.cast_write("is_admin", Value::Bool(true));
935 assert_eq!(v, Value::I64(1));
936 }
937
938 #[test]
941 fn test_complex_user_model_scenario() {
942 let mut r = AccessorRegistry::new();
943
944 r.register_cast("is_admin", CastType::Boolean);
946
947 r.register_mutator(Box::new(ClosureMutator::new("email", |v| match v {
949 Value::String(s) => Value::String(s.to_lowercase()),
950 other => other,
951 })));
952
953 r.register_accessor(Box::new(ClosureAccessor::new(
955 "full_name",
956 |v| v, )));
958
959 r.register_cast("settings", CastType::Json);
961
962 r.register_cast("created_at", CastType::DateTime);
964
965 let v = r.read("is_admin", Value::I64(1));
967 assert_eq!(v, Value::Bool(true));
968
969 let v = r.write("email", Value::String("Alice@Example.COM".to_string()));
971 assert_eq!(v, Value::String("alice@example.com".to_string()));
972
973 let v = r.read("settings", Value::String(r#"{"theme":"dark"}"#.to_string()));
975 assert!(matches!(v, Value::String(_)));
976
977 assert_eq!(r.accessor_count(), 1);
978 assert_eq!(r.mutator_count(), 1);
979 assert_eq!(r.cast_count(), 3);
980 }
981
982 #[test]
985 fn test_default_is_empty() {
986 let r = AccessorRegistry::default();
987 assert_eq!(r.accessor_count(), 0);
988 assert_eq!(r.mutator_count(), 0);
989 assert_eq!(r.cast_count(), 0);
990 }
991}