bevy-persistent 0.10.0

A Bevy helper to easily manage resources that need to persist across game sessions.
Documentation
use bevy::{
    log::LogPlugin,
    prelude::*,
    window::{
        WindowResized,
        WindowResolution,
    },
};
use bevy_persistent::prelude::*;
use serde::{
    Deserialize,
    Serialize,
};
use std::path::Path;

#[derive(Resource, Serialize, Deserialize)]
struct WindowState {
    position: (i32, i32),
    size: (u32, u32),
}

fn main() {
    let mut app = App::new();
    app.add_plugins(LogPlugin::default());

    let state_dir = dirs::state_dir()
        .map(|native_state_dir| native_state_dir.join("bevy-persistent"))
        .unwrap_or(Path::new("local").join("state"))
        .join("examples")
        .join("smart-windows");

    let persistent_window_state = Persistent::<WindowState>::builder()
        .name("window state")
        .format(StorageFormat::Toml)
        .path(state_dir.join("window-state.toml"))
        .default(WindowState { position: (0, 0), size: (800, 600) })
        .build()
        .expect("failed to initialize window state");

    let title = "I am a smart window!".to_owned();
    let position = WindowPosition::At(IVec2::from(persistent_window_state.position));
    let resolution =
        WindowResolution::new(persistent_window_state.size.0, persistent_window_state.size.1);

    app.add_plugins(DefaultPlugins.build().disable::<LogPlugin>().set(WindowPlugin {
        primary_window: Some(Window { title, position, resolution, ..Default::default() }),
        ..Default::default()
    }));

    app.insert_resource(persistent_window_state)
        .add_systems(Update, on_window_moved)
        .add_systems(Update, on_window_resized);

    app.run();
}

fn on_window_moved(
    events: MessageReader<WindowMoved>,
    windows: Single<&Window>,
    window_state: ResMut<Persistent<WindowState>>,
) {
    if !events.is_empty() {
        update_window_state(window_state, &windows);
    }
}

fn on_window_resized(
    events: MessageReader<WindowResized>,
    windows: Single<&Window>,
    window_state: ResMut<Persistent<WindowState>>,
) {
    if !events.is_empty() {
        update_window_state(window_state, &windows);
    }
}

fn update_window_state(mut window_state: ResMut<Persistent<WindowState>>, window: &Window) {
    let position = match &window.position {
        WindowPosition::At(position) => (position.x, position.y),
        _ => unreachable!(),
    };
    let size = (window.resolution.physical_width(), window.resolution.physical_height());

    if window_state.position != position || window_state.size != size {
        window_state.set(WindowState { position, size }).ok();
    }
}