use std::{cell::RefCell, collections::HashSet}; use serde::Serialize; use serde_hooks::{ser, Path}; #[test] fn test_tuple_traversing() { #[allow(clippy::enum_variant_names)] #[derive(Serialize)] enum Enum { TupleVariant((), (), (), ()), } #[derive(Serialize)] struct TupleStruct((), (), (), (), ()); #[derive(Serialize)] struct Outer { tuple: (i32, i32), nested: ((i8, i16), (u32, u64, f64)), tuple_variant: Enum, tuple_struct: TupleStruct, } let outer = Outer { tuple: (1, 2), nested: ((3, 4), (5, 6, 7.0)), tuple_variant: Enum::TupleVariant((), (), (), ()), tuple_struct: TupleStruct((), (), (), (), ()), }; struct Hooks { tuples_to_expect: RefCell>, tuple_variants_to_expect: RefCell>, tuple_structs_to_expect: RefCell>, } impl ser::Hooks for Hooks { fn on_tuple(&self, path: &Path, tpl: &mut ser::TupleScope, seq: &mut ser::SeqScope) { let path = path.borrow_str(); self.tuples_to_expect.borrow_mut().remove(path.as_str()); assert_eq!(Some(tpl.tuple_len()), seq.seq_len()); match path.as_str() { "tuple" => { assert_eq!(tpl.tuple_len(), 2); } "nested" => { assert_eq!(tpl.tuple_len(), 2); } "nested[0]" => { assert_eq!(tpl.tuple_len(), 2); } "nested[1]" => { assert_eq!(tpl.tuple_len(), 3); } "tuple_variant" => { assert_eq!(tpl.tuple_len(), 4); } "tuple_struct" => { assert_eq!(tpl.tuple_len(), 5); } _ => unreachable!("{path}"), } } fn on_tuple_variant( &self, path: &Path, ev: &mut ser::EnumVariantScope, tpl: &mut ser::TupleScope, seq: &mut ser::SeqScope, ) { let path = path.borrow_str(); self.tuple_variants_to_expect .borrow_mut() .remove(path.as_str()); assert_eq!(Some(tpl.tuple_len()), seq.seq_len()); match path.as_str() { "tuple_variant" => { assert_eq!(ev.enum_name(), "Enum"); assert_eq!(ev.variant_name(), "TupleVariant"); assert_eq!(ev.variant_index(), 0); assert_eq!(tpl.tuple_len(), 4); } _ => unreachable!("{path}"), } } fn on_tuple_struct( &self, path: &Path, tpl: &mut ser::TupleStructScope, seq: &mut ser::SeqScope, ) { let path = path.borrow_str(); self.tuple_structs_to_expect .borrow_mut() .remove(path.as_str()); assert_eq!(Some(tpl.tuple_len()), seq.seq_len()); match path.as_str() { "tuple_struct" => { assert_eq!(tpl.tuple_len(), 5); assert_eq!(tpl.struct_name(), "TupleStruct"); } _ => unreachable!("{path}"), } } } let hooks = Hooks { tuples_to_expect: RefCell::new( [ "tuple", "nested", "nested[0]", "nested[1]", "tuple_variant", "tuple_struct", ] .into(), ), tuple_variants_to_expect: RefCell::new(["tuple_variant"].into()), tuple_structs_to_expect: RefCell::new(["tuple_struct"].into()), }; serde_json::to_string(&ser::hook(&outer, &hooks)).unwrap(); assert!( hooks.tuples_to_expect.borrow().is_empty(), "following tuples were expected, but not called back about {:?}", hooks.tuples_to_expect.borrow() ); assert!( hooks.tuple_variants_to_expect.borrow().is_empty(), "following tuple variants were expected, but not called back about {:?}", hooks.tuple_variants_to_expect.borrow() ); assert!( hooks.tuple_structs_to_expect.borrow().is_empty(), "following tuple structs were expected, but not called back about {:?}", hooks.tuple_structs_to_expect.borrow() ); } #[test] fn test_modified_tuple_turns_into_seq() { #[derive(Serialize)] enum Enum { TupleVariant(u32, u32), } #[derive(Serialize)] struct TupleStruct(u32, u32); #[derive(Serialize)] struct Payload { tuple: (u32, u32), tuple_variant: Enum, tuple_struct: TupleStruct, } struct Hooks; impl ser::Hooks for Hooks { fn on_tuple(&self, path: &Path, _tpl: &mut ser::TupleScope, seq: &mut ser::SeqScope) { if path == "tuple" { seq.skip_element(0); } } fn on_tuple_variant( &self, path: &Path, _ev: &mut ser::EnumVariantScope, _tpl: &mut ser::TupleScope, seq: &mut ser::SeqScope, ) { if path == "tuple_variant" { seq.retain_element(0); } } fn on_tuple_struct( &self, path: &Path, _tpl: &mut ser::TupleStructScope, seq: &mut ser::SeqScope, ) { if path == "tuple_struct" { seq.skip_element(0); } } } let payload = Payload { tuple: (1, 2), tuple_variant: Enum::TupleVariant(3, 4), tuple_struct: TupleStruct(5, 6), }; let without_hooks = ron::to_string(&payload).unwrap(); assert_eq!( without_hooks, "(tuple:(1,2),tuple_variant:TupleVariant(3,4),tuple_struct:(5,6))" ); let with_hooks = ron::to_string(&ser::hook(&payload, &Hooks)).unwrap(); assert_eq!(with_hooks, "(tuple:[2],tuple_variant:[3],tuple_struct:[6])"); }