version-migrate
A Rust library for explicit, type-safe schema versioning and migration.
Overview
Applications that persist data locally (e.g., session data, configuration) require a robust mechanism for managing changes to the data's schema over time. Ad-hoc solutions using serde(default) or Option<T> obscure migration logic, introduce technical debt, and lack reliability.
version-migrate provides an explicit, type-safe, and developer-friendly framework for schema versioning and migration, inspired by the design philosophy of serde.
Features
- Explicit: All schema changes and migration logic must be explicitly coded and testable
- Type-Safe: Leverage Rust's type system to ensure migration paths are complete at compile time
- Robust: Provides a safe and reliable path to migrate data from any old version to the latest domain model
- Separation of Concerns: The core domain model remains completely unaware of persistence layer versioning details
- Developer Experience:
serde-like derive macro (#[derive(Versioned)]) to minimize boilerplate - Format Flexibility: Load from any serde-compatible format (JSON, TOML, YAML, etc.)
- Async Support: Async traits for migrations requiring I/O operations (database, API calls)
Installation
Add this to your Cargo.toml:
[]
= "0.1.0"
= { = "1.0", = ["derive"] }
= "1.0"
Quick Start
use ;
use ;
// Version 1.0.0
// Version 1.1.0
// Domain model (clean, version-agnostic)
// Migration from V1.0.0 to V1.1.0
// Conversion to domain model
Key Features
Save and Load
// Save versioned data to JSON
let task = TaskV1_0_0 ;
let json = migrator.save?;
// → {"version":"1.0.0","data":{"id":"1","title":"My Task"}}
// Load and automatically migrate to latest version
let task: TaskEntity = migrator.load?;
Multiple Format Support
The load_from method supports loading from any serde-compatible format (TOML, YAML, etc.):
// Load from TOML
let toml_str = r#"
version = "1.0.0"
[data]
id = "task-1"
title = "My Task"
"#;
let toml_value: Value = from_str?;
let task: TaskEntity = migrator.load_from?;
// Load from YAML
let yaml_str = r#"
version: "1.0.0"
data:
id: "task-1"
title: "My Task"
"#;
let yaml_value: Value = from_str?;
let task: TaskEntity = migrator.load_from?;
// JSON still works with the convenient load() method
let json = r#"{"version":"1.0.0","data":{"id":"task-1","title":"My Task"}}"#;
let task: TaskEntity = migrator.load?;
Automatic Migration
The migrator automatically applies all necessary migration steps:
// Even if data is V1.0.0, it will migrate through V1.1.0 → V1.2.0 → ... → Latest
let old_json = r#"{"version":"1.0.0","data":{...}}"#;
let latest: TaskEntity = migrator.load?;
Type-Safe Builder Pattern
The builder pattern ensures migration paths are complete at compile time:
define
. // Starting version
. // Must implement MigratesTo<V2> for V1
. // Must implement MigratesTo<V3> for V2
.; // Must implement IntoDomain<Domain> for V3
Async Support
For migrations requiring I/O operations (database queries, API calls), use async traits:
use ;
Migration Path Validation
Migration paths are automatically validated when registered:
let path = define
.
.
.;
let mut migrator = new;
migrator.register?; // Validates before registering
Validation checks:
- No circular paths: Prevents version A → B → A loops
- Semver ordering: Ensures versions increase (1.0.0 → 1.1.0 → 2.0.0)
Comprehensive Error Handling
All operations return Result<T, MigrationError>:
match migrator.load
Architecture
The library is split into two crates:
version-migrate: Core library with traits,Migrator, and error typesversion-migrate-macro: Procedural macro for derivingVersionedtrait
This mirrors the structure of popular libraries like serde.
Documentation
For detailed documentation, see:
Development
Running Tests
Running Checks
Building Documentation
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Acknowledgments
This library is inspired by:
serde- For its derive macro pattern and API design philosophy- Database migration tools - For the concept of explicit, versioned migrations