pub mod migrate;
pub mod versions;
use crate::game::state::GameState;
pub const CURRENT_VERSION: u32 = 4;
pub fn load_from_str(json: &str) -> GameState {
match migrate::peek_version(json) {
1 => match serde_json::from_str::<versions::v1::GameStateV1>(json) {
Ok(v1) => versions::v4::GameStateV4::from(versions::v3::GameStateV3::from(
versions::v2::GameStateV2::from(v1),
))
.into_current()
.migrate_runtime(),
Err(_) => GameState::default().migrate_runtime(),
},
2 => match serde_json::from_str::<versions::v2::GameStateV2>(json) {
Ok(v2) => versions::v4::GameStateV4::from(versions::v3::GameStateV3::from(v2))
.into_current()
.migrate_runtime(),
Err(_) => GameState::default().migrate_runtime(),
},
3 => match serde_json::from_str::<versions::v3::GameStateV3>(json) {
Ok(v3) => versions::v4::GameStateV4::from(v3)
.into_current()
.migrate_runtime(),
Err(_) => GameState::default().migrate_runtime(),
},
4 => match serde_json::from_str::<versions::v4::GameStateV4>(json) {
Ok(v4) => v4.into_current().migrate_runtime(),
Err(_) => GameState::default().migrate_runtime(),
},
_ => GameState::default().migrate_runtime(),
}
}
pub fn save_to_string(state: &GameState) -> serde_json::Result<String> {
let mut sanitized = state.clone();
if !sanitized.cuques.is_finite() {
sanitized.cuques = 0.0;
}
if !sanitized.lifetime_cuques.is_finite() {
sanitized.lifetime_cuques = 0.0;
}
if !sanitized.best_fps.is_finite() {
sanitized.best_fps = 0.0;
}
serde_json::to_string_pretty(&sanitized)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pre_versioned_json_loads_through_v1() {
let legacy = r#"{
"cuques": 1234.5,
"total_clicks": 99,
"lifetime_cuques": 1234.5,
"best_fps": 0.0,
"golden_caught": 0,
"fingerers_owned": {"index_finger": 7},
"achievements_earned": ["first_finger"],
"upgrades_earned": ["click_mult_1"],
"prestige": 0,
"total_play_ticks": 0,
"buffs": []
}"#;
let s = load_from_str(legacy);
assert_eq!(s.version, CURRENT_VERSION);
assert_eq!(s.cuques, 1234.5);
assert_eq!(s.total_clicks, 99);
assert_eq!(s.fingerer_count("index_finger"), 7);
assert!(s.has_achievement("first_finger"));
assert_eq!(s.tree.bought.len(), 1);
assert!(
s.tree
.bought
.contains(&crate::game::tree::coord::TreeCoord::ORIGIN)
);
}
#[test]
fn malformed_json_falls_back_to_default() {
let s = load_from_str("{ not valid json");
assert_eq!(s.cuques, 0.0);
assert_eq!(s.version, CURRENT_VERSION);
}
#[test]
fn round_trip_through_save_to_string_preserves_state() {
let original = GameState {
cuques: 4242.0,
total_clicks: 17,
..GameState::default()
};
let json = save_to_string(&original).expect("serialize");
let loaded = load_from_str(&json);
assert_eq!(loaded.cuques, 4242.0);
assert_eq!(loaded.total_clicks, 17);
assert_eq!(loaded.version, CURRENT_VERSION);
}
}