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
use super::*;

pub struct AutoSave<T: Serialize> {
    value: T,
    path: String,
    changed: Cell<bool>,
}

impl<T: Serialize + for<'de> Deserialize<'de> + Default> AutoSave<T> {
    pub fn load(path: &str) -> Self {
        Self {
            value: load(path).unwrap_or_else(|| {
                let value = default();
                save(path, &value);
                value
            }),
            path: path.to_owned(),
            changed: Cell::new(false),
        }
    }
}

impl<T: Serialize> AutoSave<T> {
    pub fn save(&self) {
        save(&self.path, &self.value);
    }
}

impl<T: Serialize> Deref for AutoSave<T> {
    type Target = T;
    fn deref(&self) -> &T {
        if self.changed.get() {
            self.changed.set(false);
            self.save();
        }
        &self.value
    }
}

impl<T: Serialize> DerefMut for AutoSave<T> {
    fn deref_mut(&mut self) -> &mut T {
        self.changed.set(true);
        &mut self.value
    }
}

impl<T: Serialize> Drop for AutoSave<T> {
    fn drop(&mut self) {
        self.save();
    }
}

fn save<T: Serialize>(path: &str, value: &T) {
    #[cfg(target_arch = "wasm32")]
    {
        if let Ok(Some(storage)) = web_sys::window().unwrap().local_storage() {
            storage.set_item(
                path,
                &serde_json::to_string(value).expect("Failed to serialize"),
            );
        }
    }
    #[cfg(not(target_arch = "wasm32"))]
    {
        let mut file = match std::fs::File::create(path) {
            Ok(file) => file,
            Err(e) => {
                error!("Failed to create {:?}: {}", path, e);
                return;
            }
        };
        if let Err(e) = file.write_all(
            ron::ser::to_string_pretty(value, default())
                .unwrap()
                .as_bytes(),
        ) {
            error!("Failed to save {:?}: {}", path, e);
        }
    }
}

fn load<T: for<'de> Deserialize<'de>>(path: &str) -> Option<T> {
    #[cfg(target_arch = "wasm32")]
    {
        if let Ok(Some(storage)) = web_sys::window().unwrap().local_storage() {
            match storage
                .get_item(path)
                .ok()
                .flatten()
                .map(|s| serde_json::from_str(&s))
            {
                Some(Ok(value)) => Some(value),
                Some(Err(e)) => {
                    error!("Failed to deserialize {:?}: {}", path, e);
                    None
                }
                None => None,
            }
        } else {
            None
        }
    }
    #[cfg(not(target_arch = "wasm32"))]
    {
        let file = match std::fs::File::open(path) {
            Ok(file) => file,
            Err(e) => {
                warn!("Failed to open {:?}: {}", path, e);
                return None;
            }
        };
        match ron::de::from_reader(file) {
            Ok(value) => {
                info!("Successfully loaded {:?}", path);
                Some(value)
            }
            Err(e) => {
                error!("Failed to deserialize {:?}: {}", path, e);
                None
            }
        }
    }
}