WIP: Initial commit for savefile migration #133
6 changed files with 176 additions and 9 deletions
|
@ -23,7 +23,7 @@ pub enum ApiMessage {
|
||||||
OnChargeChange(f64), // battery fill amount: 0 = empty, 1 = full
|
OnChargeChange(f64), // battery fill amount: 0 = empty, 1 = full
|
||||||
PowerVibeCheck,
|
PowerVibeCheck,
|
||||||
WaitForEmptyQueue(Callback<()>),
|
WaitForEmptyQueue(Callback<()>),
|
||||||
LoadSettings(u64, String, u64, String), // (path, name, variant, variant name)
|
LoadSettings(Option<(u64, String)>, Option<(u64, String, u64, String)>), // (legacy(game_id, name), current(path, name, variant, variant name))
|
||||||
LoadVariant(u64, String), // (variant, variant name) -- path and name assumed to be for current profile
|
LoadVariant(u64, String), // (variant, variant name) -- path and name assumed to be for current profile
|
||||||
LoadMainSettings,
|
LoadMainSettings,
|
||||||
LoadSystemSettings,
|
LoadSystemSettings,
|
||||||
|
@ -45,13 +45,21 @@ impl core::fmt::Display for ApiMessage {
|
||||||
Self::OnChargeChange(x) => write!(f, "OnChargeChange({:?})", x),
|
Self::OnChargeChange(x) => write!(f, "OnChargeChange({:?})", x),
|
||||||
Self::PowerVibeCheck => write!(f, "PowerVibeCheck"),
|
Self::PowerVibeCheck => write!(f, "PowerVibeCheck"),
|
||||||
Self::WaitForEmptyQueue(_) => write!(f, "WaitForEmptyQueue"),
|
Self::WaitForEmptyQueue(_) => write!(f, "WaitForEmptyQueue"),
|
||||||
Self::LoadSettings(path, name, variant, variant_name) => write!(f, "LoadSettings({}, {}, {}, {})", path, name, variant, variant_name),
|
Self::LoadSettings(path, name, variant, variant_name) => write!(
|
||||||
Self::LoadVariant(variant, variant_name) => write!(f, "LoadVariant({}, {})", variant, variant_name),
|
f,
|
||||||
|
"LoadSettings({}, {}, {}, {})",
|
||||||
|
path, name, variant, variant_name
|
||||||
|
),
|
||||||
|
Self::LoadVariant(variant, variant_name) => {
|
||||||
|
write!(f, "LoadVariant({}, {})", variant, variant_name)
|
||||||
|
}
|
||||||
Self::LoadMainSettings => write!(f, "LoadMainSettings"),
|
Self::LoadMainSettings => write!(f, "LoadMainSettings"),
|
||||||
Self::LoadSystemSettings => write!(f, "LoadSystemSettings"),
|
Self::LoadSystemSettings => write!(f, "LoadSystemSettings"),
|
||||||
Self::GetLimits(_) => write!(f, "GetLimits"),
|
Self::GetLimits(_) => write!(f, "GetLimits"),
|
||||||
Self::GetProvider(s, _) => write!(f, "GetProvider({})", s),
|
Self::GetProvider(s, _) => write!(f, "GetProvider({})", s),
|
||||||
Self::UploadCurrentVariant(id, user) => write!(f, "UploadCurrentVariant(id: {}, user: {})", id, user),
|
Self::UploadCurrentVariant(id, user) => {
|
||||||
|
write!(f, "UploadCurrentVariant(id: {}, user: {})", id, user)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,9 +400,7 @@ impl ApiMessageHandler {
|
||||||
messages.push(msg.to_string());
|
messages.push(msg.to_string());
|
||||||
dirty |= self.process(settings, msg);
|
dirty |= self.process(settings, msg);
|
||||||
}
|
}
|
||||||
if dirty
|
if dirty || dirty_echo {
|
||||||
|| dirty_echo
|
|
||||||
{
|
|
||||||
dirty_echo = dirty; // echo only once
|
dirty_echo = dirty; // echo only once
|
||||||
print_messages(&messages);
|
print_messages(&messages);
|
||||||
// run on_set
|
// run on_set
|
||||||
|
@ -498,7 +504,61 @@ impl ApiMessageHandler {
|
||||||
self.on_empty.push(callback);
|
self.on_empty.push(callback);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
ApiMessage::LoadSettings(id, name, variant_id, variant_name) => {
|
ApiMessage::LoadSettings(legacy_settings, current_settings) => {
|
||||||
|
/* Migration steps:
|
||||||
|
1. Modify to the frontend to send the game ID in this message (`id` here is the app
|
||||||
|
ID).
|
||||||
|
2. Change game ID to app ID.
|
||||||
|
3. (Create and) call function to merge OldSettingsJson with existing FileJson, or
|
||||||
|
use existing `From<OldSettingsJson> for FileJson` if this game doesn't have a
|
||||||
|
save in the new format yet.
|
||||||
|
4. Let the rest of the code below work its magic.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ===== migration logic =====
|
||||||
|
if legacy_settings.is_some() {
|
||||||
|
let (legacy_game_id, legacy_name) = legacy_settings.unwrap();
|
||||||
|
|
||||||
|
let legacy_file_path = format!("{legacy_game_id}.json");
|
||||||
|
let legacy_file = crate::persist::OldSettingsJson::open(legacy_file_path)
|
||||||
|
.expect("should be able to deserialzie legacy savefile format"); // TODO: don't panic on fail.
|
||||||
|
|
||||||
|
if current_settings.is_some() {
|
||||||
|
// A savefile in both the legacy and current format exist.
|
||||||
|
let (id, name, variant_id, variant_name) = current_settings.unwrap();
|
||||||
|
let path = format!("{}.ron", id);
|
||||||
|
|
||||||
|
// 1. Parse `legacy_file` to into a settings variant.
|
||||||
|
let migrated_settings_variant: crate::persist::SettingsJson =
|
||||||
|
legacy_file.into();
|
||||||
|
// 2. Insert the variant into the current settings file.
|
||||||
|
match settings.load_file(
|
||||||
|
path.into(),
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
variant_id,
|
||||||
|
variant_name,
|
||||||
|
false,
|
||||||
|
) {
|
||||||
|
Ok(success) => log::info!("Loaded settings file? {}", success),
|
||||||
|
Err(e) => log::warn!("Load file err: {}", e),
|
||||||
|
}
|
||||||
|
settings.add_variant() //TODO: This.
|
||||||
|
} else {
|
||||||
|
// A savefile in the current format doesn't exist yet.
|
||||||
|
// TODO: Parse it to the new format and save it.
|
||||||
|
|
||||||
|
let app_id_from_game_id: u64;
|
||||||
|
let new_path = format!("{app_id_from_game_id}.ron");
|
||||||
|
|
||||||
|
let migrated_settings: crate::persist::FileJson = legacy_file.into();
|
||||||
|
migrated_settings.save(new_path);
|
||||||
|
}
|
||||||
|
} else if current_settings.is_some() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
// ===========================
|
||||||
|
|
||||||
let path = format!("{}.ron", id);
|
let path = format!("{}.ron", id);
|
||||||
if let Err(e) = settings.on_unload() {
|
if let Err(e) = settings.on_unload() {
|
||||||
print_errors("LoadSettings on_unload()", e);
|
print_errors("LoadSettings on_unload()", e);
|
||||||
|
|
|
@ -36,6 +36,14 @@ impl From<ron::error::Error> for RonError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<serde_json::Error> for RonError {
|
||||||
|
fn from(_value: serde_json::Error) -> Self {
|
||||||
|
RonError::General(ron::Error::Message(String::from(
|
||||||
|
"TODO: make error handling in migration logic good. Specifically, make it so a json error can be used as a RonError instead of just returning this string",
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ron::error::SpannedError> for RonError {
|
impl From<ron::error::SpannedError> for RonError {
|
||||||
fn from(value: ron::error::SpannedError) -> Self {
|
fn from(value: ron::error::SpannedError) -> Self {
|
||||||
Self::Spanned(value)
|
Self::Spanned(value)
|
||||||
|
|
4
backend/src/persist/migration.rs
Normal file
4
backend/src/persist/migration.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod gpu;
|
||||||
|
pub mod settings;
|
||||||
|
|
||||||
|
pub const APP_ID_UNKNOWN: u64 = u64::MAX;
|
26
backend/src/persist/migration/gpu.rs
Normal file
26
backend/src/persist/migration/gpu.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::persist::{GpuJson, MinMaxJson};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct OldGpuJson {
|
||||||
|
pub fast_ppt: Option<u64>,
|
||||||
|
pub slow_ppt: Option<u64>,
|
||||||
|
pub clock_limits: Option<MinMaxJson<u64>>,
|
||||||
|
pub slow_memory: bool,
|
||||||
|
pub root: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OldGpuJson> for GpuJson {
|
||||||
|
fn from(old_gpu: OldGpuJson) -> Self {
|
||||||
|
Self {
|
||||||
|
fast_ppt: old_gpu.fast_ppt,
|
||||||
|
slow_ppt: old_gpu.slow_ppt,
|
||||||
|
tdp: None,
|
||||||
|
tdp_boost: None,
|
||||||
|
clock_limits: old_gpu.clock_limits.clone(),
|
||||||
|
memory_clock: None,
|
||||||
|
root: old_gpu.root.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
backend/src/persist/migration/settings.rs
Normal file
67
backend/src/persist/migration/settings.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::persist::{
|
||||||
|
BatteryJson, CpuJson, DriverJson, FileJson, SerdeError, SettingsJson, LATEST_VERSION,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::gpu::OldGpuJson;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct OldOnEventJson {
|
||||||
|
pub on_save: Option<String>,
|
||||||
|
pub on_load: Option<String>,
|
||||||
|
pub on_set: Option<String>,
|
||||||
|
pub on_resume: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct OldSettingsJson {
|
||||||
|
pub version: u64,
|
||||||
|
pub name: String,
|
||||||
|
pub persistent: bool,
|
||||||
|
pub cpus: Vec<CpuJson>,
|
||||||
|
pub gpu: OldGpuJson,
|
||||||
|
pub battery: BatteryJson,
|
||||||
|
pub provider: Option<DriverJson>,
|
||||||
|
pub events: Option<OldOnEventJson>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OldSettingsJson> for SettingsJson {
|
||||||
|
fn from(old_settings: OldSettingsJson) -> Self {
|
||||||
|
Self {
|
||||||
|
version: old_settings.version,
|
||||||
|
name: format!("{} (migrated)", old_settings.name.clone()),
|
||||||
|
variant: 0,
|
||||||
|
persistent: old_settings.persistent,
|
||||||
|
cpus: old_settings.cpus.clone(),
|
||||||
|
gpu: old_settings.gpu.clone().into(),
|
||||||
|
battery: old_settings.battery.clone(),
|
||||||
|
provider: old_settings.provider.clone(),
|
||||||
|
tags: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OldSettingsJson> for FileJson {
|
||||||
|
fn from(old_settings: OldSettingsJson) -> Self {
|
||||||
|
let mut variants = HashMap::new();
|
||||||
|
let variant = SettingsJson::from(old_settings.clone());
|
||||||
|
variants.insert(0, variant);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
version: LATEST_VERSION,
|
||||||
|
name: old_settings.name.clone(),
|
||||||
|
app_id: super::APP_ID_UNKNOWN, // `u64::MAX`, sentinel value.
|
||||||
|
variants,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OldSettingsJson {
|
||||||
|
pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Self, SerdeError> {
|
||||||
|
let mut file = std::fs::File::open(path).map_err(SerdeError::Io)?;
|
||||||
|
serde_json::from_reader(&mut file).map_err(|e| SerdeError::Serde(e.into()))
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ mod error;
|
||||||
mod file;
|
mod file;
|
||||||
mod general;
|
mod general;
|
||||||
mod gpu;
|
mod gpu;
|
||||||
|
mod migration;
|
||||||
|
|
||||||
pub use battery::{BatteryEventJson, BatteryJson};
|
pub use battery::{BatteryEventJson, BatteryJson};
|
||||||
pub use cpu::CpuJson;
|
pub use cpu::CpuJson;
|
||||||
|
@ -12,7 +13,8 @@ pub use driver::DriverJson;
|
||||||
pub use file::FileJson;
|
pub use file::FileJson;
|
||||||
pub use general::{MinMaxJson, SettingsJson};
|
pub use general::{MinMaxJson, SettingsJson};
|
||||||
pub use gpu::GpuJson;
|
pub use gpu::GpuJson;
|
||||||
|
pub use migration::{gpu::*, settings::*};
|
||||||
|
|
||||||
pub use error::SerdeError;
|
pub use error::{RonError, SerdeError};
|
||||||
|
|
||||||
pub const LATEST_VERSION: u64 = 0;
|
pub const LATEST_VERSION: u64 = 0;
|
||||||
|
|
Loading…
Reference in a new issue