Files
dropbreak/examples/migration.rs.incomplete
2018-05-10 17:00:43 +02:00

184 lines
5.0 KiB
Plaintext

/* This file is a complete work in progress! And is meant to illustrate a possible way of
* migrating databases with Rustbreak.
*
* Currently the major challenge is to statically define the upgrade process.
*
* This is currently done by using the `upgrading_macro`.
*
* The idea is to read the 'version' tag in the file, load it into a simple tag struct and
* read the version from there. This version is then used to dynamically get the corresponding
* struct type. This then gets converted to the latest version.
*/
extern crate rustbreak;
#[macro_use] extern crate serde_derive;
use rustbreak::FileDatabase;
use rustbreak::deser::Ron;
mod data {
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
pub enum Version {
First, Second, Third,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Versioning {
pub version: Version
}
pub mod v1 {
use data::Version;
use std::collections::HashMap;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Players {
version: Version,
pub players: HashMap<i32, String>,
}
impl Players {
pub fn new() -> Players {
Players {
version: Version::First,
players: HashMap::new(),
}
}
}
}
pub mod v2 {
use data::Version;
use std::collections::HashMap;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Player {
pub name: String,
pub level: i32,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Players {
version: Version,
pub players: HashMap<i32, Player>,
}
impl Players {
pub fn new() -> Players {
Players {
version: Version::Second,
players: HashMap::new(),
}
}
}
}
pub mod v3 {
use data::Version;
use std::collections::HashMap;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Player {
pub name: String,
pub score: i64
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Players {
version: Version,
pub players: HashMap<i32, Player>,
}
impl Players {
pub fn new() -> Players {
Players {
version: Version::Third,
players: HashMap::new(),
}
}
}
}
pub fn convert_v1_v2(input: v1::Players) -> v2::Players {
let mut new = v2::Players::new();
for (key, player) in input.players {
new.players.insert(key,
v2::Player {
name: player,
level: 1
});
}
new
}
pub fn convert_v2_v3(input: v2::Players) -> v3::Players {
let mut new = v3::Players::new();
for (key, player) in input.players {
new.players.insert(key,
v3::Player {
name: player.name,
score: 0
});
}
new
}
}
macro_rules! migration_rules {
( $name:ident -> $res:ty { $( $t:ty => $conv:path ),* $(,)* } ) => {
fn $name<T: 'static>(input: T) -> Option<$res> {
fn inner(mut input: Box<std::any::Any>) -> Option<$res> {
println!("We have: {:#?}", input);
$(
{
let mut maybe_in = None;
if let Some(out) = input.downcast_ref::<$t>() {
maybe_in = Some(Box::new($conv(out.clone())));
}
if let Some(inp) = maybe_in {
input = inp;
}
}
)*
if let Ok(out) = input.downcast::<$res>() {
return Some(*out);
}
None
}
inner(Box::new(input))
}
}
}
migration_rules! {
update_database -> data::v3::Players {
data::v1::Players => data::convert_v1_v2,
data::v2::Players => data::convert_v2_v3,
}
}
fn get_version() -> data::Version {
let db = FileDatabase::<data::Versioning, Ron>::from_path("migration.ron", data::Versioning { version: data::Version::First }).unwrap();
db.load().unwrap();
db.read(|ver| ver.version).unwrap()
}
fn main() {
use data::v3::Players;
let version = get_version();
// Let's check if there are any updates
let updated_data = update_database(database.get_data(false).unwrap()).unwrap();
let database = FileDatabase::<Players, Ron>::from_path("migration.ron", updated_data).unwrap();
println!("{:#?}", database);
}