Skip to main content

cuqueclicker_lib/save/
migrate.rs

1//! Version-peeking helper for the save migration chain.
2//!
3//! Splitting this out of `mod.rs` keeps the dispatch in [`super::load_from_str`]
4//! readable and gives version detection its own focused test surface.
5
6use serde::Deserialize;
7
8/// Read the top-level `"version"` field from a save JSON without
9/// deserializing the rest of the document. Returns `1` if the field is
10/// absent, non-numeric, or the JSON itself is malformed — pre-versioned
11/// saves (everything written by `main` before this branch) are V1 by
12/// definition.
13pub fn peek_version(json: &str) -> u32 {
14    #[derive(Deserialize)]
15    struct VersionPeek {
16        #[serde(default = "default_version")]
17        version: u32,
18    }
19    fn default_version() -> u32 {
20        1
21    }
22    serde_json::from_str::<VersionPeek>(json)
23        .map(|v| v.version)
24        .unwrap_or(1)
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30
31    #[test]
32    fn returns_1_when_field_absent() {
33        assert_eq!(peek_version(r#"{"cuques": 100}"#), 1);
34    }
35
36    #[test]
37    fn returns_1_on_invalid_json() {
38        assert_eq!(peek_version("not json"), 1);
39    }
40
41    #[test]
42    fn reads_the_field_when_present() {
43        assert_eq!(peek_version(r#"{"version": 7, "cuques": 100}"#), 7);
44    }
45
46    #[test]
47    fn returns_1_when_version_is_non_numeric() {
48        // serde rejects "1" as u32 → fall back to 1 rather than crash.
49        assert_eq!(peek_version(r#"{"version": "1"}"#), 1);
50    }
51}