version-migrate 0.20.0

Explicit, type-safe schema versioning and migration for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
//! # version-migrate
//!
//! A library for explicit, type-safe schema versioning and migration.
//!
//! ## Features
//!
//! - **Type-safe migrations**: Define migrations between versions using traits
//! - **Validation**: Automatic validation of migration paths (circular path detection, version ordering)
//! - **Multi-format support**: Load from JSON, TOML, YAML, or any serde-compatible format
//! - **Legacy data support**: Automatic fallback for data without version information
//! - **Vec support**: Migrate collections of versioned entities
//! - **Hierarchical structures**: Support for nested versioned entities
//! - **Async migrations**: Optional async support for I/O-heavy migrations
//!
//! ## Basic Example
//!
//! ```ignore
//! use version_migrate::{Versioned, MigratesTo, IntoDomain, Migrator};
//! use serde::{Serialize, Deserialize};
//!
//! // Version 1.0.0
//! #[derive(Serialize, Deserialize, Versioned)]
//! #[versioned(version = "1.0.0")]
//! struct TaskV1_0_0 {
//!     id: String,
//!     title: String,
//! }
//!
//! // Version 1.1.0
//! #[derive(Serialize, Deserialize, Versioned)]
//! #[versioned(version = "1.1.0")]
//! struct TaskV1_1_0 {
//!     id: String,
//!     title: String,
//!     description: Option<String>,
//! }
//!
//! // Domain model
//! struct TaskEntity {
//!     id: String,
//!     title: String,
//!     description: Option<String>,
//! }
//!
//! impl MigratesTo<TaskV1_1_0> for TaskV1_0_0 {
//!     fn migrate(self) -> TaskV1_1_0 {
//!         TaskV1_1_0 {
//!             id: self.id,
//!             title: self.title,
//!             description: None,
//!         }
//!     }
//! }
//!
//! impl IntoDomain<TaskEntity> for TaskV1_1_0 {
//!     fn into_domain(self) -> TaskEntity {
//!         TaskEntity {
//!             id: self.id,
//!             title: self.title,
//!             description: self.description,
//!         }
//!     }
//! }
//! ```
//!
//! ## Working with Collections (Vec)
//!
//! ```ignore
//! // Save multiple versioned entities
//! let tasks = vec![
//!     TaskV1_0_0 { id: "1".into(), title: "Task 1".into() },
//!     TaskV1_0_0 { id: "2".into(), title: "Task 2".into() },
//! ];
//! let json = migrator.save_vec(tasks)?;
//!
//! // Load and migrate multiple entities
//! let domains: Vec<TaskEntity> = migrator.load_vec("task", &json)?;
//! ```
//!
//! ## Legacy Data Support
//!
//! Handle data that was created before versioning was introduced:
//!
//! ```ignore
//! // Legacy data without version information
//! let legacy_json = r#"{"id": "task-1", "title": "Legacy Task"}"#;
//!
//! // Automatically treats legacy data as the first version and migrates
//! let domain: TaskEntity = migrator.load_with_fallback("task", legacy_json)?;
//!
//! // Also works with properly versioned data
//! let versioned_json = r#"{"version":"1.0.0","data":{"id":"task-1","title":"My Task"}}"#;
//! let domain: TaskEntity = migrator.load_with_fallback("task", versioned_json)?;
//! ```
//!
//! ## Hierarchical Structures
//!
//! For complex configurations with nested versioned entities:
//!
//! ```ignore
//! #[derive(Serialize, Deserialize, Versioned)]
//! #[versioned(version = "1.0.0")]
//! struct ConfigV1 {
//!     setting: SettingV1,
//!     items: Vec<ItemV1>,
//! }
//!
//! #[derive(Serialize, Deserialize, Versioned)]
//! #[versioned(version = "2.0.0")]
//! struct ConfigV2 {
//!     setting: SettingV2,
//!     items: Vec<ItemV2>,
//! }
//!
//! impl MigratesTo<ConfigV2> for ConfigV1 {
//!     fn migrate(self) -> ConfigV2 {
//!         ConfigV2 {
//!             // Migrate nested entities
//!             setting: self.setting.migrate(),
//!             items: self.items.into_iter()
//!                 .map(|item| item.migrate())
//!                 .collect(),
//!         }
//!     }
//! }
//! ```
//!
//! ## Design Philosophy
//!
//! This library follows the **explicit versioning** approach:
//!
//! - Each version has its own type (V1, V2, V3, etc.)
//! - Migration logic is explicit and testable
//! - Version changes are tracked in code
//! - Root-level versioning ensures consistency
//!
//! This differs from ProtoBuf's "append-only" approach but allows for:
//! - Schema refactoring and cleanup
//! - Type-safe migration paths
//! - Clear version history in code

use serde::{Deserialize, Serialize};

pub mod dir_storage;
pub mod errors;
pub mod forward;
mod migrator;
pub mod storage;
pub mod versioned_dir;
pub mod versioned_file;

// Re-export the derive macros
pub use version_migrate_macro::Versioned;

// Re-export Queryable derive macro (same name as trait is OK in Rust)
#[doc(inline)]
pub use version_migrate_macro::Queryable as DeriveQueryable;

// Re-export VersionMigrate derive macro
#[doc(inline)]
pub use version_migrate_macro::VersionMigrate;
/// Creates a migration path with simplified syntax.
///
/// This macro provides a concise way to define migration paths between versioned types.
/// Use this when you need just the path without creating a Migrator instance.
///
/// # Syntax
///
/// Basic usage:
/// ```ignore
/// migrate_path!("entity", [V1, V2, V3])
/// ```
///
/// With custom version/data keys:
/// ```ignore
/// migrate_path!("entity", [V1, V2, V3], version_key = "v", data_key = "d")
/// ```
///
/// # Arguments
///
/// * `entity` - The entity name as a string literal (e.g., `"user"`, `"task"`)
/// * `versions` - A list of version types in migration order (e.g., `[V1, V2, V3]`)
/// * `version_key` - (Optional) Custom key for the version field (default: `"version"`)
/// * `data_key` - (Optional) Custom key for the data field (default: `"data"`)
///
/// # Examples
///
/// ```ignore
/// use version_migrate::{migrate_path, Migrator};
///
/// // Simple two-step migration
/// let path = migrate_path!("task", [TaskV1, TaskV2]);
///
/// // Multi-step migration
/// let path = migrate_path!("task", [TaskV1, TaskV2, TaskV3]);
///
/// // Many versions (arbitrary length supported)
/// let path = migrate_path!("task", [TaskV1, TaskV2, TaskV3, TaskV4, TaskV5, TaskV6]);
///
/// // With custom keys
/// let path = migrate_path!("task", [TaskV1, TaskV2], version_key = "v", data_key = "d");
///
/// // Register with migrator
/// let mut migrator = Migrator::new();
/// migrator.register(path).unwrap();
/// ```
///
/// # Generated Code
///
/// The macro expands to the equivalent builder pattern:
/// ```ignore
/// // migrate_path!("entity", [V1, V2])
/// // expands to:
/// Migrator::define("entity")
///     .from::<V1>()
///     .into::<V2>()
/// ```
#[macro_export]
macro_rules! migrate_path {
    // Basic: migrate_path!("entity", [V1, V2, V3, ...])
    ($entity:expr, [$first:ty, $($rest:ty),+ $(,)?]) => {
        $crate::migrator_vec_helper!($first; $($rest),+; $entity)
    };

    // With custom keys: migrate_path!("entity", [V1, V2, ...], version_key = "v", data_key = "d")
    ($entity:expr, [$first:ty, $($rest:ty),+ $(,)?], version_key = $version_key:expr, data_key = $data_key:expr) => {
        $crate::migrator_vec_helper_with_keys!($first; $($rest),+; $entity; $version_key; $data_key)
    };
}

/// Helper macro for Vec notation without custom keys
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_helper {
    // Base case: two versions left
    ($first:ty; $last:ty; $entity:expr) => {
        $crate::Migrator::define($entity)
            .from::<$first>()
            .into::<$last>()
    };

    // Recursive case: more than two versions
    ($first:ty; $second:ty, $($rest:ty),+; $entity:expr) => {
        $crate::migrator_vec_build_steps!($first; $($rest),+; $entity; {
            $crate::Migrator::define($entity).from::<$first>().step::<$second>()
        })
    };
}

/// Helper for building all steps, then applying final .into()
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_build_steps {
    // Final case: last version, call .into()
    ($first:ty; $last:ty; $entity:expr; { $builder:expr }) => {
        $builder.into::<$last>()
    };

    // Recursive case: add .step() and continue
    ($first:ty; $current:ty, $($rest:ty),+; $entity:expr; { $builder:expr }) => {
        $crate::migrator_vec_build_steps!($first; $($rest),+; $entity; {
            $builder.step::<$current>()
        })
    };
}

/// Helper macro for Vec notation with custom keys
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_helper_with_keys {
    // Base case: two versions left
    ($first:ty; $last:ty; $entity:expr; $version_key:expr; $data_key:expr) => {
        $crate::Migrator::define($entity)
            .with_keys($version_key, $data_key)
            .from::<$first>()
            .into::<$last>()
    };

    // Recursive case: more than two versions
    ($first:ty; $second:ty, $($rest:ty),+; $entity:expr; $version_key:expr; $data_key:expr) => {
        $crate::migrator_vec_build_steps_with_keys!($first; $($rest),+; $entity; $version_key; $data_key; {
            $crate::Migrator::define($entity).with_keys($version_key, $data_key).from::<$first>().step::<$second>()
        })
    };
}

/// Helper for building all steps with custom keys, then applying final .into()
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_build_steps_with_keys {
    // Final case: last version, call .into()
    ($first:ty; $last:ty; $entity:expr; $version_key:expr; $data_key:expr; { $builder:expr }) => {
        $builder.into::<$last>()
    };

    // Recursive case: add .step() and continue
    ($first:ty; $current:ty, $($rest:ty),+; $entity:expr; $version_key:expr; $data_key:expr; { $builder:expr }) => {
        $crate::migrator_vec_build_steps_with_keys!($first; $($rest),+; $entity; $version_key; $data_key; {
            $builder.step::<$current>()
        })
    };
}

/// Helper macro for Vec notation with save support (without custom keys)
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_helper_with_save {
    // Base case: two versions left
    ($first:ty; $last:ty; $entity:expr) => {
        $crate::Migrator::define($entity)
            .from::<$first>()
            .into_with_save::<$last>()
    };

    // Recursive case: more than two versions
    ($first:ty; $second:ty, $($rest:ty),+; $entity:expr) => {
        $crate::migrator_vec_build_steps_with_save!($first; $($rest),+; $entity; {
            $crate::Migrator::define($entity).from::<$first>().step::<$second>()
        })
    };
}

/// Helper for building all steps with save support, then applying final .into_with_save()
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_build_steps_with_save {
    // Final case: last version, call .into_with_save()
    ($first:ty; $last:ty; $entity:expr; { $builder:expr }) => {
        $builder.into_with_save::<$last>()
    };

    // Recursive case: add .step() and continue
    ($first:ty; $current:ty, $($rest:ty),+; $entity:expr; { $builder:expr }) => {
        $crate::migrator_vec_build_steps_with_save!($first; $($rest),+; $entity; {
            $builder.step::<$current>()
        })
    };
}

/// Helper macro for Vec notation with custom keys and save support
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_helper_with_keys_and_save {
    // Base case: two versions left
    ($first:ty; $last:ty; $entity:expr; $version_key:expr; $data_key:expr) => {
        $crate::Migrator::define($entity)
            .with_keys($version_key, $data_key)
            .from::<$first>()
            .into_with_save::<$last>()
    };

    // Recursive case: more than two versions
    ($first:ty; $second:ty, $($rest:ty),+; $entity:expr; $version_key:expr; $data_key:expr) => {
        $crate::migrator_vec_build_steps_with_keys_and_save!($first; $($rest),+; $entity; $version_key; $data_key; {
            $crate::Migrator::define($entity).with_keys($version_key, $data_key).from::<$first>().step::<$second>()
        })
    };
}

/// Helper for building all steps with custom keys and save support, then applying final .into_with_save()
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_build_steps_with_keys_and_save {
    // Final case: last version, call .into_with_save()
    ($first:ty; $last:ty; $entity:expr; $version_key:expr; $data_key:expr; { $builder:expr }) => {
        $builder.into_with_save::<$last>()
    };

    // Recursive case: add .step() and continue
    ($first:ty; $current:ty, $($rest:ty),+; $entity:expr; $version_key:expr; $data_key:expr; { $builder:expr }) => {
        $crate::migrator_vec_build_steps_with_keys_and_save!($first; $($rest),+; $entity; $version_key; $data_key; {
            $builder.step::<$current>()
        })
    };
}

/// Creates a fully initialized `Migrator` with registered migration paths.
///
/// This macro creates a `Migrator` instance and registers one or more migration paths,
/// returning a ready-to-use migrator. This is the recommended way to create a migrator
/// as it's more concise than manually calling `Migrator::new()` and `register()` for each path.
///
/// # Syntax
///
/// Single path:
/// ```ignore
/// migrator!("entity" => [V1, V2, V3])
/// ```
///
/// Multiple paths:
/// ```ignore
/// migrator!(
///     "task" => [TaskV1, TaskV2, TaskV3],
///     "user" => [UserV1, UserV2]
/// )
/// ```
///
/// Single path with custom keys:
/// ```ignore
/// migrator!(
///     "task" => [TaskV1, TaskV2], version_key = "v", data_key = "d"
/// )
/// ```
///
/// Multiple paths with custom keys (requires `@keys` prefix):
/// ```ignore
/// migrator!(
///     @keys version_key = "v", data_key = "d";
///     "task" => [TaskV1, TaskV2],
///     "user" => [UserV1, UserV2]
/// )
/// ```
///
/// # Examples
///
/// ```ignore
/// use version_migrate::migrator;
///
/// // Single entity migration
/// let migrator = migrator!("task" => [TaskV1, TaskV2, TaskV3]).unwrap();
///
/// // Multiple entities
/// let migrator = migrator!(
///     "task" => [TaskV1, TaskV2],
///     "user" => [UserV1, UserV2]
/// ).unwrap();
///
/// // Single entity with custom keys
/// let migrator = migrator!(
///     "task" => [TaskV1, TaskV2], version_key = "v", data_key = "d"
/// ).unwrap();
///
/// // Multiple entities with custom keys
/// let migrator = migrator!(
///     @keys version_key = "v", data_key = "d";
///     "task" => [TaskV1, TaskV2],
///     "user" => [UserV1, UserV2]
/// ).unwrap();
///
/// // Now ready to use
/// let domain: TaskEntity = migrator.load("task", json_str)?;
/// ```
///
/// # Returns
///
/// Returns `Result<Migrator, MigrationError>`. The migrator is ready to use if `Ok`.
#[macro_export]
macro_rules! migrator {
    // Single path with custom keys and save support (most specific)
    ($entity:expr => [$first:ty, $($rest:ty),+ $(,)?], version_key = $version_key:expr, data_key = $data_key:expr, save = true) => {{
        let mut migrator = $crate::Migrator::new();
        let path = $crate::migrator_vec_helper_with_keys_and_save!($first; $($rest),+; $entity; $version_key; $data_key);
        migrator.register(path).map(|_| migrator)
    }};

    // Single path with custom keys (no save)
    ($entity:expr => [$first:ty, $($rest:ty),+ $(,)?], version_key = $version_key:expr, data_key = $data_key:expr) => {{
        let mut migrator = $crate::Migrator::new();
        let path = $crate::migrate_path!($entity, [$first, $($rest),+], version_key = $version_key, data_key = $data_key);
        migrator.register(path).map(|_| migrator)
    }};

    // Multiple paths with custom keys and save support
    (@keys version_key = $version_key:expr, data_key = $data_key:expr, save = true; $($entity:expr => [$first:ty, $($rest:ty),+ $(,)?]),+ $(,)?) => {{
        let mut migrator = $crate::Migrator::new();
        $(
            let path = $crate::migrator_vec_helper_with_keys_and_save!($first; $($rest),+; $entity; $version_key; $data_key);
            migrator.register(path)?;
        )+
        Ok::<$crate::Migrator, $crate::MigrationError>(migrator)
    }};

    // Multiple paths with custom keys (no save)
    (@keys version_key = $version_key:expr, data_key = $data_key:expr; $($entity:expr => [$first:ty, $($rest:ty),+ $(,)?]),+ $(,)?) => {{
        let mut migrator = $crate::Migrator::new();
        $(
            let path = $crate::migrate_path!($entity, [$first, $($rest),+], version_key = $version_key, data_key = $data_key);
            migrator.register(path)?;
        )+
        Ok::<$crate::Migrator, $crate::MigrationError>(migrator)
    }};

    // Single path with save support (no custom keys)
    ($entity:expr => [$first:ty, $($rest:ty),+ $(,)?], save = true) => {{
        let mut migrator = $crate::Migrator::new();
        let path = $crate::migrator_vec_helper_with_save!($first; $($rest),+; $entity);
        migrator.register(path).map(|_| migrator)
    }};

    // Single path without custom keys (no save)
    ($entity:expr => [$first:ty, $($rest:ty),+ $(,)?]) => {{
        let mut migrator = $crate::Migrator::new();
        let path = $crate::migrate_path!($entity, [$first, $($rest),+]);
        migrator.register(path).map(|_| migrator)
    }};

    // Multiple paths with save support (no custom keys)
    (@save; $($entity:expr => [$first:ty, $($rest:ty),+ $(,)?]),+ $(,)?) => {{
        let mut migrator = $crate::Migrator::new();
        $(
            let path = $crate::migrator_vec_helper_with_save!($first; $($rest),+; $entity);
            migrator.register(path)?;
        )+
        Ok::<$crate::Migrator, $crate::MigrationError>(migrator)
    }};

    // Multiple paths without custom keys (no save, must come last)
    ($($entity:expr => [$first:ty, $($rest:ty),+ $(,)?]),+ $(,)?) => {{
        let mut migrator = $crate::Migrator::new();
        $(
            let path = $crate::migrate_path!($entity, [$first, $($rest),+]);
            migrator.register(path)?;
        )+
        Ok::<$crate::Migrator, $crate::MigrationError>(migrator)
    }};
}

// Re-export error types
pub use errors::{IoOperationKind, MigrationError, StoreError};

// Re-export migrator types
pub use migrator::{ConfigMigrator, MigrationPath, Migrator};

// Re-export storage types
pub use local_store::{AtomicWriteConfig, FileStorageStrategy, FormatStrategy, LoadBehavior};
pub use storage::FileStorage;

// Re-export dir_storage types
pub use dir_storage::DirStorage;
pub use local_store::{DirStorageStrategy, FilenameEncoding};

#[cfg(feature = "async")]
pub use dir_storage::AsyncDirStorage;

// Re-export versioned wrappers (raw IO delegated to local_store)
pub use versioned_dir::VersionedDirStorage;
pub use versioned_file::VersionedFileStorage;

#[cfg(feature = "async")]
pub use versioned_dir::VersionedAsyncDirStorage;

// Re-export forward compatibility types
pub use forward::{ForwardContext, Forwardable};

// Re-export paths types
pub use local_store::{AppPaths, PathStrategy, PrefPath};

// Re-export async-trait for user convenience
#[cfg(feature = "async")]
pub use async_trait::async_trait;

/// A trait for versioned data schemas.
///
/// This trait marks a type as representing a specific version of a data schema.
/// It should be derived using `#[derive(Versioned)]` along with the `#[versioned(version = "x.y.z")]` attribute.
///
/// # Custom Keys
///
/// You can customize the serialization keys:
///
/// ```ignore
/// #[derive(Versioned)]
/// #[versioned(
///     version = "1.0.0",
///     version_key = "schema_version",
///     data_key = "payload"
/// )]
/// struct Task { ... }
/// // Serializes to: {"schema_version":"1.0.0","payload":{...}}
/// ```
pub trait Versioned {
    /// The semantic version of this schema.
    const VERSION: &'static str;

    /// The key name for the version field in serialized data.
    /// Defaults to "version".
    const VERSION_KEY: &'static str = "version";

    /// The key name for the data field in serialized data.
    /// Defaults to "data".
    const DATA_KEY: &'static str = "data";
}

/// Defines explicit migration logic from one version to another.
///
/// Implementing this trait establishes a migration path from `Self` (the source version)
/// to `T` (the target version).
pub trait MigratesTo<T: Versioned>: Versioned {
    /// Migrates from the current version to the target version.
    fn migrate(self) -> T;
}

/// Converts a versioned DTO into the application's domain model.
///
/// This trait should be implemented on the latest version of a DTO to convert
/// it into the clean, version-agnostic domain model.
pub trait IntoDomain<D>: Versioned {
    /// Converts this versioned data into the domain model.
    fn into_domain(self) -> D;
}

/// Converts a domain model back into a versioned DTO.
///
/// This trait should be implemented on versioned DTOs to enable conversion
/// from the domain model back to the versioned format for serialization.
///
/// # Example
///
/// ```ignore
/// impl FromDomain<TaskEntity> for TaskV1_1_0 {
///     fn from_domain(domain: TaskEntity) -> Self {
///         TaskV1_1_0 {
///             id: domain.id,
///             title: domain.title,
///             description: domain.description,
///         }
///     }
/// }
/// ```
pub trait FromDomain<D>: Versioned + Serialize {
    /// Converts a domain model into this versioned format.
    fn from_domain(domain: D) -> Self;
}

/// Associates a domain entity with its latest versioned representation.
///
/// This trait enables automatic saving of domain entities using their latest version.
/// It should typically be derived using the `#[version_migrate]` attribute macro.
///
/// # Example
///
/// ```ignore
/// #[derive(Serialize, Deserialize)]
/// #[version_migrate(entity = "task", latest = TaskV1_1_0)]
/// struct TaskEntity {
///     id: String,
///     title: String,
///     description: Option<String>,
/// }
///
/// // Now you can save entities directly
/// let entity = TaskEntity { ... };
/// let json = migrator.save_entity(entity)?;
/// ```
pub trait LatestVersioned: Sized {
    /// The latest versioned type for this entity.
    type Latest: Versioned + Serialize + FromDomain<Self>;

    /// The entity name used for migration paths.
    const ENTITY_NAME: &'static str;

    /// Whether this entity supports saving functionality.
    /// When `false` (default), uses `into()` for read-only access.
    /// When `true`, uses `into_with_save()` to enable domain entity saving.
    const SAVE: bool = false;

    /// Converts this domain entity into its latest versioned format.
    fn to_latest(self) -> Self::Latest {
        Self::Latest::from_domain(self)
    }
}

/// Marks a domain type as queryable, associating it with an entity name.
///
/// This trait enables `ConfigMigrator` to automatically determine which entity
/// path to use when querying or updating data.
///
/// # Example
///
/// ```ignore
/// impl Queryable for TaskEntity {
///     const ENTITY_NAME: &'static str = "task";
/// }
///
/// let tasks: Vec<TaskEntity> = config.query("tasks")?;
/// ```
pub trait Queryable {
    /// The entity name used to look up migration paths in the `Migrator`.
    const ENTITY_NAME: &'static str;
}

/// Async version of `MigratesTo` for migrations requiring I/O operations.
///
/// Use this trait when migrations need to perform asynchronous operations
/// such as database queries or API calls.
#[cfg(feature = "async")]
#[async_trait::async_trait]
pub trait AsyncMigratesTo<T: Versioned>: Versioned + Send {
    /// Asynchronously migrates from the current version to the target version.
    ///
    /// # Errors
    ///
    /// Returns `MigrationError` if the migration fails.
    async fn migrate(self) -> Result<T, MigrationError>;
}

/// Async version of `IntoDomain` for domain conversions requiring I/O operations.
///
/// Use this trait when converting to the domain model requires asynchronous
/// operations such as fetching additional data from external sources.
#[cfg(feature = "async")]
#[async_trait::async_trait]
pub trait AsyncIntoDomain<D>: Versioned + Send {
    /// Asynchronously converts this versioned data into the domain model.
    ///
    /// # Errors
    ///
    /// Returns `MigrationError` if the conversion fails.
    async fn into_domain(self) -> Result<D, MigrationError>;
}

/// A wrapper for serialized data that includes explicit version information.
///
/// This struct is used for persistence to ensure that the version of the data
/// is always stored alongside the data itself.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct VersionedWrapper<T> {
    /// The semantic version of the data.
    pub version: String,
    /// The actual data.
    pub data: T,
}

impl<T> VersionedWrapper<T> {
    /// Creates a new versioned wrapper with the specified version and data.
    pub fn new(version: String, data: T) -> Self {
        Self { version, data }
    }
}

impl<T: Versioned> VersionedWrapper<T> {
    /// Creates a wrapper from a versioned value, automatically extracting its version.
    pub fn from_versioned(data: T) -> Self {
        Self {
            version: T::VERSION.to_string(),
            data,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
    struct TestData {
        value: String,
    }

    impl Versioned for TestData {
        const VERSION: &'static str = "1.0.0";
    }

    #[test]
    fn test_versioned_wrapper_from_versioned() {
        let data = TestData {
            value: "test".to_string(),
        };
        let wrapper = VersionedWrapper::from_versioned(data);

        assert_eq!(wrapper.version, "1.0.0");
        assert_eq!(wrapper.data.value, "test");
    }

    #[test]
    fn test_versioned_wrapper_new() {
        let data = TestData {
            value: "manual".to_string(),
        };
        let wrapper = VersionedWrapper::new("2.0.0".to_string(), data);

        assert_eq!(wrapper.version, "2.0.0");
        assert_eq!(wrapper.data.value, "manual");
    }

    #[test]
    fn test_versioned_wrapper_serialization() {
        let data = TestData {
            value: "serialize_test".to_string(),
        };
        let wrapper = VersionedWrapper::from_versioned(data);

        // Serialize
        let json = serde_json::to_string(&wrapper).expect("Serialization failed");

        // Deserialize
        let deserialized: VersionedWrapper<TestData> =
            serde_json::from_str(&json).expect("Deserialization failed");

        assert_eq!(deserialized.version, "1.0.0");
        assert_eq!(deserialized.data.value, "serialize_test");
    }

    #[test]
    fn test_versioned_wrapper_with_complex_data() {
        #[derive(Serialize, Deserialize, Debug, PartialEq)]
        struct ComplexData {
            id: u64,
            name: String,
            tags: Vec<String>,
            metadata: Option<String>,
        }

        impl Versioned for ComplexData {
            const VERSION: &'static str = "3.2.1";
        }

        let data = ComplexData {
            id: 42,
            name: "complex".to_string(),
            tags: vec!["tag1".to_string(), "tag2".to_string()],
            metadata: Some("meta".to_string()),
        };

        let wrapper = VersionedWrapper::from_versioned(data);
        assert_eq!(wrapper.version, "3.2.1");
        assert_eq!(wrapper.data.id, 42);
        assert_eq!(wrapper.data.tags.len(), 2);
    }

    #[test]
    fn test_versioned_wrapper_clone() {
        let data = TestData {
            value: "clone_test".to_string(),
        };
        let wrapper = VersionedWrapper::from_versioned(data);
        let cloned = wrapper.clone();

        assert_eq!(cloned.version, wrapper.version);
        assert_eq!(cloned.data.value, wrapper.data.value);
    }

    #[test]
    fn test_versioned_wrapper_debug() {
        let data = TestData {
            value: "debug".to_string(),
        };
        let wrapper = VersionedWrapper::from_versioned(data);
        let debug_str = format!("{:?}", wrapper);

        assert!(debug_str.contains("1.0.0"));
        assert!(debug_str.contains("debug"));
    }
}