world_transmuter_engine/
convert.rs

1use crate::{JCompound, JValue, JValueMut};
2use java_string::{JavaStr, JavaString};
3use std::cmp::Ordering;
4use std::collections::BTreeMap;
5use std::rc::Rc;
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
8pub struct DataVersion {
9    version: u32,
10    step: u32,
11}
12
13impl DataVersion {
14    pub fn new(version: u32, step: u32) -> Self {
15        Self { version, step }
16    }
17
18    #[inline]
19    pub fn get_version(&self) -> u32 {
20        self.version
21    }
22
23    #[inline]
24    pub fn get_step(&self) -> u32 {
25        self.step
26    }
27}
28
29impl From<u32> for DataVersion {
30    fn from(value: u32) -> Self {
31        DataVersion::new(value, 0)
32    }
33}
34
35pub trait MapDataConverterFunc {
36    fn convert(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion);
37}
38
39pub fn map_data_converter_func<'a, F>(func: F) -> impl MapDataConverterFunc + 'a
40where
41    F: Fn(&mut JCompound, DataVersion, DataVersion) + 'a,
42{
43    struct DataConverterFuncImpl<F>(F);
44    impl<F> MapDataConverterFunc for DataConverterFuncImpl<F>
45    where
46        F: Fn(&mut JCompound, DataVersion, DataVersion),
47    {
48        fn convert(
49            &self,
50            data: &mut JCompound,
51            from_version: DataVersion,
52            to_version: DataVersion,
53        ) {
54            (self.0)(data, from_version, to_version)
55        }
56    }
57    DataConverterFuncImpl(func)
58}
59
60impl<T: MapDataConverterFunc + ?Sized> MapDataConverterFunc for &T {
61    fn convert(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
62        T::convert(self, data, from_version, to_version)
63    }
64}
65
66impl<T: MapDataConverterFunc + ?Sized> MapDataConverterFunc for Box<T> {
67    fn convert(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
68        T::convert(self, data, from_version, to_version)
69    }
70}
71
72pub struct MapDataConverter<F: MapDataConverterFunc> {
73    to_version: DataVersion,
74    conversion_func: F,
75}
76
77impl<F: MapDataConverterFunc> MapDataConverter<F> {
78    pub fn new(to_version: impl Into<DataVersion>, conversion_func: F) -> Self {
79        Self {
80            to_version: to_version.into(),
81            conversion_func,
82        }
83    }
84
85    #[inline]
86    pub fn get_to_version(&self) -> DataVersion {
87        self.to_version
88    }
89
90    pub fn convert(
91        &self,
92        data: &mut JCompound,
93        from_version: impl Into<DataVersion>,
94        to_version: impl Into<DataVersion>,
95    ) {
96        self.conversion_func
97            .convert(data, from_version.into(), to_version.into())
98    }
99}
100
101pub trait ValueDataConverterFunc {
102    fn convert(&self, data: &mut JValueMut, from_version: DataVersion, to_version: DataVersion);
103}
104
105pub fn value_data_converter_func<'a, F>(func: F) -> impl ValueDataConverterFunc + 'a
106where
107    F: Fn(&mut JValueMut, DataVersion, DataVersion) + 'a,
108{
109    struct DataConverterFuncImpl<F>(F);
110    impl<F> ValueDataConverterFunc for DataConverterFuncImpl<F>
111    where
112        F: Fn(&mut JValueMut, DataVersion, DataVersion),
113    {
114        fn convert(
115            &self,
116            data: &mut JValueMut,
117            from_version: DataVersion,
118            to_version: DataVersion,
119        ) {
120            (self.0)(data, from_version, to_version)
121        }
122    }
123    DataConverterFuncImpl(func)
124}
125
126impl<T: ValueDataConverterFunc + ?Sized> ValueDataConverterFunc for &T {
127    fn convert(&self, data: &mut JValueMut, from_version: DataVersion, to_version: DataVersion) {
128        T::convert(self, data, from_version, to_version)
129    }
130}
131
132impl<T: ValueDataConverterFunc + ?Sized> ValueDataConverterFunc for Box<T> {
133    fn convert(&self, data: &mut JValueMut, from_version: DataVersion, to_version: DataVersion) {
134        T::convert(self, data, from_version, to_version)
135    }
136}
137
138pub struct ValueDataConverter<F: ValueDataConverterFunc> {
139    to_version: DataVersion,
140    conversion_func: F,
141}
142
143impl<F: ValueDataConverterFunc> ValueDataConverter<F> {
144    pub fn new(to_version: impl Into<DataVersion>, conversion_func: F) -> Self {
145        Self {
146            to_version: to_version.into(),
147            conversion_func,
148        }
149    }
150
151    #[inline]
152    pub fn get_to_version(&self) -> DataVersion {
153        self.to_version
154    }
155
156    pub fn convert(
157        &self,
158        data: &mut JValueMut,
159        from_version: impl Into<DataVersion>,
160        to_version: impl Into<DataVersion>,
161    ) {
162        self.conversion_func
163            .convert(data, from_version.into(), to_version.into())
164    }
165}
166
167pub trait DynamicDataConverterFunc {
168    fn convert(&self, data: &mut JValue, from_version: DataVersion, to_version: DataVersion);
169}
170
171pub fn dynamic_data_converter_func<'a, F>(func: F) -> impl DynamicDataConverterFunc + 'a
172where
173    F: Fn(&mut JValue, DataVersion, DataVersion) + 'a,
174{
175    struct DataConverterFuncImpl<F>(F);
176    impl<F> DynamicDataConverterFunc for DataConverterFuncImpl<F>
177    where
178        F: Fn(&mut JValue, DataVersion, DataVersion),
179    {
180        fn convert(&self, data: &mut JValue, from_version: DataVersion, to_version: DataVersion) {
181            (self.0)(data, from_version, to_version)
182        }
183    }
184    DataConverterFuncImpl(func)
185}
186
187impl<T: DynamicDataConverterFunc + ?Sized> DynamicDataConverterFunc for &T {
188    fn convert(&self, data: &mut JValue, from_version: DataVersion, to_version: DataVersion) {
189        T::convert(self, data, from_version, to_version)
190    }
191}
192
193impl<T: DynamicDataConverterFunc + ?Sized> DynamicDataConverterFunc for Box<T> {
194    fn convert(&self, data: &mut JValue, from_version: DataVersion, to_version: DataVersion) {
195        T::convert(self, data, from_version, to_version)
196    }
197}
198
199pub struct DynamicDataConverter<F: DynamicDataConverterFunc> {
200    to_version: DataVersion,
201    conversion_func: F,
202}
203
204impl<F: DynamicDataConverterFunc> DynamicDataConverter<F> {
205    pub fn new(to_version: impl Into<DataVersion>, conversion_func: F) -> Self {
206        Self {
207            to_version: to_version.into(),
208            conversion_func,
209        }
210    }
211
212    #[inline]
213    pub fn get_to_version(&self) -> DataVersion {
214        self.to_version
215    }
216
217    pub fn convert(
218        &self,
219        data: &mut JValue,
220        from_version: impl Into<DataVersion>,
221        to_version: impl Into<DataVersion>,
222    ) {
223        self.conversion_func
224            .convert(data, from_version.into(), to_version.into())
225    }
226}
227
228macro_rules! impl_traits {
229    ($converter:ident, $converter_func_trait:ident) => {
230        impl<F: $converter_func_trait> core::fmt::Debug for $converter<F> {
231            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> std::fmt::Result {
232                write!(
233                    f,
234                    concat!(stringify!($converter), "{{{:?}}}"),
235                    self.to_version
236                )
237            }
238        }
239
240        impl<F: $converter_func_trait> PartialEq for $converter<F> {
241            fn eq(&self, other: &Self) -> bool {
242                self.to_version == other.to_version
243            }
244        }
245
246        impl<F: $converter_func_trait> Eq for $converter<F> {}
247
248        impl<F: $converter_func_trait> PartialOrd for $converter<F> {
249            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
250                Some(self.cmp(other))
251            }
252        }
253
254        impl<F: $converter_func_trait> Ord for $converter<F> {
255            fn cmp(&self, other: &Self) -> Ordering {
256                self.to_version.cmp(&other.to_version)
257            }
258        }
259    };
260}
261
262impl_traits!(MapDataConverter, MapDataConverterFunc);
263impl_traits!(ValueDataConverter, ValueDataConverterFunc);
264impl_traits!(DynamicDataConverter, DynamicDataConverterFunc);
265
266pub struct ConversionError {
267    pub message: String,
268}
269
270pub type Result<T> = core::result::Result<T, ConversionError>;
271
272pub trait AbstractMapDataType {
273    fn convert(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion);
274}
275
276impl<T: AbstractMapDataType> AbstractMapDataType for &T {
277    fn convert(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
278        T::convert(self, data, from_version, to_version)
279    }
280}
281
282impl<T: AbstractMapDataType> AbstractMapDataType for std::sync::RwLock<T> {
283    fn convert(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
284        let this = self.read().unwrap();
285        T::convert(&*this, data, from_version, to_version)
286    }
287}
288
289pub trait AbstractValueDataType {
290    fn convert(&self, data: &mut JValueMut, from_version: DataVersion, to_version: DataVersion);
291}
292
293impl<T: AbstractValueDataType> AbstractValueDataType for &T {
294    fn convert(&self, data: &mut JValueMut, from_version: DataVersion, to_version: DataVersion) {
295        T::convert(self, data, from_version, to_version)
296    }
297}
298
299impl<T: AbstractValueDataType> AbstractValueDataType for std::sync::RwLock<T> {
300    fn convert(&self, data: &mut JValueMut, from_version: DataVersion, to_version: DataVersion) {
301        let this = self.read().unwrap();
302        T::convert(&*this, data, from_version, to_version)
303    }
304}
305
306pub trait AbstractDynamicDataType {
307    fn convert(&self, data: &mut JValue, from_version: DataVersion, to_version: DataVersion);
308}
309
310impl<T: AbstractDynamicDataType> AbstractDynamicDataType for &T {
311    fn convert(&self, data: &mut JValue, from_version: DataVersion, to_version: DataVersion) {
312        T::convert(self, data, from_version, to_version)
313    }
314}
315
316impl<T: AbstractDynamicDataType> AbstractDynamicDataType for std::sync::RwLock<T> {
317    fn convert(&self, data: &mut JValue, from_version: DataVersion, to_version: DataVersion) {
318        let this = self.read().unwrap();
319        T::convert(&*this, data, from_version, to_version)
320    }
321}
322
323macro_rules! structure_converters {
324    ($ty:ident, $field_name:ident, $data_converter:ident, $converter_func:ident) => {
325        impl<'a> $ty<'a> {
326            pub fn add_structure_converter(
327                &mut self,
328                version: impl Into<DataVersion>,
329                func: impl $converter_func + 'a,
330            ) {
331                let dyn_box: Box<dyn $converter_func> = Box::new(func);
332                let converter = $data_converter::new(version, dyn_box);
333                let index = self.$field_name.binary_search(&converter);
334                let index = match index {
335                    Ok(i) => i,
336                    Err(i) => i,
337                };
338                self.$field_name.insert(index, converter);
339            }
340        }
341    };
342}
343
344macro_rules! version_list {
345    ($ty:ident, $method_name:ident, $field_name:ident, $element_type:ty) => {
346        impl<'a> $ty<'a> {
347            pub fn $method_name(&mut self, version: impl Into<DataVersion>, value: $element_type) {
348                self.$field_name
349                    .entry(version.into())
350                    .or_default()
351                    .push(Box::new(value));
352            }
353        }
354    };
355}
356
357type DynMapDataConverterFunc<'a> = Box<dyn MapDataConverterFunc + 'a>;
358
359pub struct MapDataType<'a> {
360    pub name: String,
361    structure_converters: Vec<MapDataConverter<DynMapDataConverterFunc<'a>>>,
362    structure_walkers: BTreeMap<DataVersion, Vec<Box<dyn MapDataWalker + 'a>>>,
363    structure_hooks: BTreeMap<DataVersion, Vec<Box<dyn MapDataHook + 'a>>>,
364}
365structure_converters!(
366    MapDataType,
367    structure_converters,
368    MapDataConverter,
369    MapDataConverterFunc
370);
371version_list!(
372    MapDataType,
373    add_structure_walker,
374    structure_walkers,
375    impl MapDataWalker + 'a
376);
377version_list!(
378    MapDataType,
379    add_structure_hook,
380    structure_hooks,
381    impl MapDataHook + 'a
382);
383impl<'a> MapDataType<'a> {
384    pub fn new(name: impl Into<String>) -> Self {
385        Self {
386            name: name.into(),
387            structure_converters: Vec::new(),
388            structure_walkers: BTreeMap::new(),
389            structure_hooks: BTreeMap::new(),
390        }
391    }
392}
393
394impl<'a> AbstractMapDataType for MapDataType<'a> {
395    fn convert(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
396        for converter in &self.structure_converters {
397            if converter.get_to_version() <= from_version {
398                continue;
399            }
400            if converter.get_to_version() > to_version {
401                break;
402            }
403
404            let hooks = self
405                .structure_hooks
406                .range(..=converter.get_to_version())
407                .next_back();
408            if let Some((_, hooks)) = hooks {
409                for hook in hooks {
410                    hook.pre_hook(data, from_version, to_version);
411                }
412            }
413
414            converter.convert(data, from_version, to_version);
415
416            // possibly new data format, update hooks
417            let hooks = self.structure_hooks.range(..=to_version).next_back();
418            if let Some((_, hooks)) = hooks {
419                for hook in hooks.iter().rev() {
420                    hook.post_hook(data, from_version, to_version);
421                }
422            }
423        }
424
425        let hooks = self.structure_hooks.range(..=to_version).next_back();
426        if let Some((_, hooks)) = hooks {
427            for hook in hooks {
428                hook.pre_hook(data, from_version, to_version);
429            }
430        }
431
432        let walkers = self.structure_walkers.range(..=to_version).next_back();
433        if let Some((_, walkers)) = walkers {
434            for walker in walkers {
435                walker.walk(data, from_version, to_version);
436            }
437        }
438
439        if let Some((_, hooks)) = hooks {
440            for hook in hooks.iter().rev() {
441                hook.post_hook(data, from_version, to_version);
442            }
443        }
444    }
445}
446
447type DynValueDataConverterFunc<'a> = Box<dyn ValueDataConverterFunc + 'a>;
448
449pub struct ObjectDataType<'a> {
450    pub name: String,
451    converters: Vec<ValueDataConverter<DynValueDataConverterFunc<'a>>>,
452    structure_hooks: BTreeMap<DataVersion, Vec<Box<dyn ValueDataHook + 'a>>>,
453}
454structure_converters!(
455    ObjectDataType,
456    converters,
457    ValueDataConverter,
458    ValueDataConverterFunc
459);
460version_list!(
461    ObjectDataType,
462    add_structure_hook,
463    structure_hooks,
464    impl ValueDataHook + 'a
465);
466
467impl<'a> ObjectDataType<'a> {
468    pub fn new(name: impl Into<String>) -> Self {
469        Self {
470            name: name.into(),
471            converters: Vec::new(),
472            structure_hooks: BTreeMap::new(),
473        }
474    }
475}
476
477impl<'a> AbstractValueDataType for ObjectDataType<'a> {
478    fn convert(&self, data: &mut JValueMut, from_version: DataVersion, to_version: DataVersion) {
479        for converter in &self.converters {
480            if converter.get_to_version() <= from_version {
481                continue;
482            }
483            if converter.get_to_version() > to_version {
484                break;
485            }
486
487            let hooks = self
488                .structure_hooks
489                .range(..=converter.get_to_version())
490                .next_back();
491            if let Some((_, hooks)) = hooks {
492                for hook in hooks {
493                    hook.pre_hook(data, from_version, to_version);
494                }
495            }
496
497            converter.convert(data, from_version, to_version);
498
499            // possibly new data format, update hooks
500            let hooks = self.structure_hooks.range(..=to_version).next_back();
501            if let Some((_, hooks)) = hooks {
502                for hook in hooks.iter().rev() {
503                    hook.post_hook(data, from_version, to_version);
504                }
505            }
506        }
507    }
508}
509
510type DynDynamicDataConverterFunc<'a> = Box<dyn DynamicDataConverterFunc + 'a>;
511
512pub struct DynamicDataType<'a> {
513    pub name: String,
514    structure_converters: Vec<DynamicDataConverter<DynDynamicDataConverterFunc<'a>>>,
515    structure_walkers: BTreeMap<DataVersion, Vec<Box<dyn DynamicDataWalker + 'a>>>,
516    structure_hooks: BTreeMap<DataVersion, Vec<Box<dyn DynamicDataHook + 'a>>>,
517}
518structure_converters!(
519    DynamicDataType,
520    structure_converters,
521    DynamicDataConverter,
522    DynamicDataConverterFunc
523);
524version_list!(
525    DynamicDataType,
526    add_structure_walker,
527    structure_walkers,
528    impl DynamicDataWalker + 'a
529);
530version_list!(
531    DynamicDataType,
532    add_structure_hook,
533    structure_hooks,
534    impl DynamicDataHook + 'a
535);
536
537impl<'a> DynamicDataType<'a> {
538    pub fn new(name: impl Into<String>) -> Self {
539        Self {
540            name: name.into(),
541            structure_converters: Vec::new(),
542            structure_walkers: BTreeMap::new(),
543            structure_hooks: BTreeMap::new(),
544        }
545    }
546}
547
548impl<'a> AbstractDynamicDataType for DynamicDataType<'a> {
549    fn convert(&self, data: &mut JValue, from_version: DataVersion, to_version: DataVersion) {
550        for converter in &self.structure_converters {
551            if converter.get_to_version() <= from_version {
552                continue;
553            }
554            if converter.get_to_version() > to_version {
555                break;
556            }
557
558            let hooks = self
559                .structure_hooks
560                .range(..=converter.get_to_version())
561                .next_back();
562            if let Some((_, hooks)) = hooks {
563                for hook in hooks {
564                    hook.pre_hook(data, from_version, to_version);
565                }
566            }
567
568            converter.convert(data, from_version, to_version);
569
570            // possibly new data format, update hooks
571            let hooks = self.structure_hooks.range(..=to_version).next_back();
572            if let Some((_, hooks)) = hooks {
573                for hook in hooks.iter().rev() {
574                    hook.post_hook(data, from_version, to_version);
575                }
576            }
577        }
578
579        let hooks = self.structure_hooks.range(..=to_version).next_back();
580        if let Some((_, hooks)) = hooks {
581            for hook in hooks {
582                hook.pre_hook(data, from_version, to_version);
583            }
584        }
585
586        let walkers = self.structure_walkers.range(..=to_version).next_back();
587        if let Some((_, walkers)) = walkers {
588            for walker in walkers {
589                walker.walk(data, from_version, to_version);
590            }
591        }
592
593        if let Some((_, hooks)) = hooks {
594            for hook in hooks.iter().rev() {
595                hook.post_hook(data, from_version, to_version);
596            }
597        }
598    }
599}
600
601type WalkersById<'a> = Vec<Rc<dyn MapDataWalker + 'a>>;
602
603pub struct IdDataType<'a> {
604    pub name: String,
605    structure_converters: Vec<MapDataConverter<DynMapDataConverterFunc<'a>>>,
606    structure_walkers: BTreeMap<DataVersion, Vec<Box<dyn MapDataWalker + 'a>>>,
607    structure_hooks: BTreeMap<DataVersion, Vec<Box<dyn MapDataHook + 'a>>>,
608    walkers_by_id: BTreeMap<JavaString, BTreeMap<DataVersion, WalkersById<'a>>>,
609}
610structure_converters!(
611    IdDataType,
612    structure_converters,
613    MapDataConverter,
614    MapDataConverterFunc
615);
616version_list!(
617    IdDataType,
618    add_structure_walker,
619    structure_walkers,
620    impl MapDataWalker + 'a
621);
622version_list!(
623    IdDataType,
624    add_structure_hook,
625    structure_hooks,
626    impl MapDataHook + 'a
627);
628
629impl<'a> IdDataType<'a> {
630    pub fn new(name: impl Into<String>) -> Self {
631        Self {
632            name: name.into(),
633            structure_converters: Vec::new(),
634            structure_walkers: BTreeMap::new(),
635            structure_hooks: BTreeMap::new(),
636            walkers_by_id: BTreeMap::new(),
637        }
638    }
639
640    pub fn add_converter_for_id(
641        &mut self,
642        id: impl Into<JavaString>,
643        version: impl Into<DataVersion>,
644        converter_func: impl MapDataConverterFunc + 'a,
645    ) {
646        let id_str = id.into();
647        self.add_structure_converter(
648            version,
649            map_data_converter_func(move |data, from_version, to_version| {
650                if matches!(data.get("id"), Some(valence_nbt::Value::String(str)) if str == &id_str)
651                {
652                    converter_func.convert(data, from_version, to_version);
653                }
654            }),
655        );
656    }
657
658    pub fn add_walker_for_id(
659        &mut self,
660        version: impl Into<DataVersion>,
661        id: impl Into<JavaString>,
662        walker: impl MapDataWalker + 'a,
663    ) {
664        self.walkers_by_id
665            .entry(id.into())
666            .or_default()
667            .entry(version.into())
668            .or_default()
669            .push(Rc::new(walker));
670    }
671
672    pub fn copy_walkers(
673        &mut self,
674        version: impl Into<DataVersion> + Clone,
675        from_id: impl AsRef<JavaStr>,
676        to_id: impl Into<JavaString> + Clone,
677    ) {
678        if let Some(from_versions) = self.walkers_by_id.get(from_id.as_ref()) {
679            if let Some((_, from_walkers)) =
680                from_versions.range(..=version.clone().into()).next_back()
681            {
682                for walker in from_walkers.clone() {
683                    self.walkers_by_id
684                        .entry(to_id.clone().into())
685                        .or_default()
686                        .entry(version.clone().into())
687                        .or_default()
688                        .push(walker);
689                }
690            }
691        }
692    }
693}
694
695impl<'a> AbstractMapDataType for IdDataType<'a> {
696    fn convert(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
697        for converter in &self.structure_converters {
698            if converter.get_to_version() <= from_version {
699                continue;
700            }
701            if converter.get_to_version() > to_version {
702                break;
703            }
704
705            let hooks = self
706                .structure_hooks
707                .range(..=converter.get_to_version())
708                .next_back();
709            if let Some((_, hooks)) = hooks {
710                for hook in hooks {
711                    hook.pre_hook(data, from_version, to_version);
712                }
713            }
714
715            converter.convert(data, from_version, to_version);
716
717            // possibly new data format, update hooks
718            let hooks = self.structure_hooks.range(..=to_version).next_back();
719            if let Some((_, hooks)) = hooks {
720                for hook in hooks {
721                    hook.post_hook(data, from_version, to_version);
722                }
723            }
724        }
725
726        // run pre hooks
727
728        let hooks = self.structure_hooks.range(..=to_version).next_back();
729        if let Some((_, hooks)) = hooks {
730            for hook in hooks.iter().rev() {
731                hook.pre_hook(data, from_version, to_version);
732            }
733        }
734
735        // run all walkers
736
737        let walkers = self.structure_walkers.range(..=to_version).next_back();
738        if let Some((_, walkers)) = walkers {
739            for walker in walkers {
740                walker.walk(data, from_version, to_version);
741            }
742        }
743
744        if let Some(valence_nbt::Value::String(id)) = data.get("id") {
745            if let Some(walkers_by_version) = self.walkers_by_id.get(id) {
746                if let Some((_, walkers)) = walkers_by_version.range(..=to_version).next_back() {
747                    for walker in walkers {
748                        walker.walk(data, from_version, to_version);
749                    }
750                }
751            }
752        }
753
754        // run post hooks
755
756        if let Some((_, hooks)) = hooks {
757            for hook in hooks.iter().rev() {
758                hook.post_hook(data, from_version, to_version);
759            }
760        }
761    }
762}
763
764pub trait MapDataHook {
765    fn pre_hook(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion);
766    fn post_hook(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion);
767}
768
769pub trait ValueDataHook {
770    fn pre_hook(&self, data: &mut JValueMut, from_version: DataVersion, to_version: DataVersion);
771    fn post_hook(&self, data: &mut JValueMut, from_version: DataVersion, to_version: DataVersion);
772}
773
774pub trait DynamicDataHook {
775    fn pre_hook(&self, data: &mut JValue, from_version: DataVersion, to_version: DataVersion);
776    fn post_hook(&self, data: &mut JValue, from_version: DataVersion, to_version: DataVersion);
777}
778
779pub trait MapDataWalker {
780    fn walk(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion);
781}
782
783pub fn map_data_walker<'a, F>(func: F) -> impl MapDataWalker + 'a
784where
785    F: Fn(&mut JCompound, DataVersion, DataVersion) + 'a,
786{
787    struct MapDataWalkerImpl<F>(F);
788    impl<F> MapDataWalker for MapDataWalkerImpl<F>
789    where
790        F: Fn(&mut JCompound, DataVersion, DataVersion),
791    {
792        fn walk(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
793            (self.0)(data, from_version, to_version)
794        }
795    }
796    MapDataWalkerImpl(func)
797}
798
799pub trait DynamicDataWalker {
800    fn walk(&self, data: &mut JValue, from_version: DataVersion, to_version: DataVersion);
801}
802
803pub fn dynamic_data_walker<'a, F>(func: F) -> impl DynamicDataWalker + 'a
804where
805    F: Fn(&mut JValue, DataVersion, DataVersion) + 'a,
806{
807    struct DynamicDataWalkerImpl<F>(F);
808    impl<F> DynamicDataWalker for DynamicDataWalkerImpl<F>
809    where
810        F: Fn(&mut JValue, DataVersion, DataVersion),
811    {
812        fn walk(&self, data: &mut JValue, from_version: DataVersion, to_version: DataVersion) {
813            (self.0)(data, from_version, to_version)
814        }
815    }
816    DynamicDataWalkerImpl(func)
817}