update/downgrade v1 to V2

This commit is contained in:
Alexis Delhaie
2020-09-24 10:47:06 +02:00
commit 4664b63862
8 changed files with 686 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

323
Cargo.lock generated Normal file
View File

@@ -0,0 +1,323 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "arrayref"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arrayvec"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "base64"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
[[package]]
name = "blake2b_simd"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "chrono"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b"
dependencies = [
"num-integer",
"num-traits",
"time",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
"cfg-if",
"lazy_static",
]
[[package]]
name = "dirs"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
dependencies = [
"cfg-if",
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "getrandom"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "itoa"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
[[package]]
name = "num-integer"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
dependencies = [
"autocfg",
]
[[package]]
name = "platform-dirs"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1e6f10c0c97e3d27b298374c2c67a057216c98e0a86c44df6bcd1f02bacbe38"
dependencies = [
"dirs",
]
[[package]]
name = "proc-macro2"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "redox_users"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
dependencies = [
"getrandom",
"redox_syscall",
"rust-argon2",
]
[[package]]
name = "rust-argon2"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19"
dependencies = [
"base64",
"blake2b_simd",
"constant_time_eq",
"crossbeam-utils",
]
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "save-updater-src"
version = "0.1.0"
dependencies = [
"chrono",
"platform-dirs",
"serde",
"serde_json",
"winres",
]
[[package]]
name = "serde"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]]
name = "toml"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
dependencies = [
"serde",
]
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "winres"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff4fb510bbfe5b8992ff15f77a2e6fe6cf062878f0eda00c0f44963a807ca5dc"
dependencies = [
"toml",
]

20
Cargo.toml Normal file
View File

@@ -0,0 +1,20 @@
[package]
name = "save-updater-src"
version = "0.1.0"
authors = ["Alexis Delhaie <alexis.delhaie@yahoo.com>"]
edition = "2018"
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
platform-dirs = "0.2.0"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
chrono = "0.4.15"
[build-dependencies]
winres = "0.1"
[profile.release]
opt-level = 3

9
build.rs Normal file
View File

@@ -0,0 +1,9 @@
extern crate winres;
fn main() {
if cfg!(target_os = "windows") {
let mut res = winres::WindowsResource::new();
res.set_icon("icon.ico");
res.compile().unwrap();
}
}

BIN
icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

155
src/main.rs Normal file
View File

@@ -0,0 +1,155 @@
mod model_version_1;
mod model_version_2;
pub use crate::model_version_1::SaveVersion1;
pub use crate::model_version_2::downgrade_to_v1;
pub use crate::model_version_2::upgrade_to_v2;
pub use crate::model_version_2::SaveVersion2;
use platform_dirs::{AppDirs, AppUI};
use serde_json::Value;
use std::env;
use std::fs::{create_dir_all, File};
use std::io::prelude::*;
use std::io::{self, BufReader, Read};
use std::time::SystemTime;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() >= 2 {
let verb = &args[1];
match verb.as_ref() {
"update" => update_cmd(),
"help" => help_cmd(false, ""),
"downgrade" => {
if args.len() >= 3 {
let allow_downgrade = confirm(
"Do you really want to downgrade your save? \
You may experience data lost",
)
.unwrap();
if allow_downgrade {
let version: u64 = args[2]
.parse::<u64>()
.expect("ERROR: Cannot get version number");
downgrade_cmd(version);
} else {
println!("Operation aborted");
}
} else {
help_cmd(true, "ERROR: The version number is missing")
}
}
_ => help_cmd(true, "ERROR: This verb is not allowed"),
}
} else {
help_cmd(true, "ERROR: The verb arg is missing");
}
}
fn help_cmd(has_error: bool, err: &str) {
if has_error {
eprintln!("{}", err);
println!("");
}
println!("Usage: save-updater.exe <verb> [args]\n");
println!("List of verbs:");
println!(" update Update the Chronos file save");
println!(" downgrade <version> Downgrade to a version");
println!(" help Show this screen");
}
fn downgrade_cmd(version: u64) {
let json_file = open_save_file().expect("ERROR: Cannot open save file");
let v: Value = serde_json::from_str(&json_file).expect("ERROR: Cannot parse save file");
let current_version: u64 = v["version"]
.as_u64()
.expect("ERROR: Error while parsing save version");
if version == (current_version - 1) && version > 0 {
add_to_backup(&json_file).expect("ERROR: Failed to create backup");
match version {
1 => {
let s: SaveVersion2 =
serde_json::from_str(&json_file).expect("ERROR: Cannot parse save file");
let new_save = downgrade_to_v1(s);
let json =
serde_json::to_string(&new_save).expect("ERROR: Fail to build new save file");
save_to_file(&json, "data.json").expect("ERROR: Fail to save updated file");
println!("INFO: Save file has been downgraded");
}
_ => eprintln!("ERROR: Version not supported"),
}
} else {
eprintln!("ERROR: Only downgrading to the previous version is allowed");
}
}
fn update_cmd() {
let json_file = open_save_file().expect("ERROR: Cannot open save file");
let v: Value = serde_json::from_str(&json_file).expect("ERROR: Cannot parse save file");
if v["version"] == 1 {
add_to_backup(&json_file).expect("ERROR: Failed to create backup");
let s: SaveVersion1 =
serde_json::from_str(&json_file).expect("ERROR: Cannot parse save file");
let new_save = start_upgrade_from_v1(s);
let json = serde_json::to_string(&new_save).expect("ERROR: Fail to build new save file");
save_to_file(&json, "data.json").expect("ERROR: Fail to save updated file");
println!("INFO: Save file is up-to-date");
} else {
println!("Save already up-to-date");
}
}
fn open_save_file() -> std::result::Result<std::string::String, &'static str> {
let app_dirs = AppDirs::new(Some("Chronos"), AppUI::CommandLine)
.expect("ERROR: Cannot get application data folder");
let path = app_dirs.config_dir.join("data").join("data.json");
if path.exists() {
let file = File::open(path).expect("ERROR: cannot open file");
let mut buf_reader = BufReader::new(file);
let mut contents = String::new();
buf_reader
.read_to_string(&mut contents)
.expect("cannot read file");
Ok(contents)
} else {
Err("Save file not found")
}
}
fn save_to_file(content: &String, filename: &str) -> std::io::Result<()> {
let app_dirs = AppDirs::new(Some("Chronos"), AppUI::CommandLine)
.expect("ERROR: Cannot get application data folder");
let path = app_dirs.config_dir.join("data").join(filename);
let mut file = File::create(path)?;
file.write_all(content.as_bytes())?;
Ok(())
}
fn confirm(msg: &str) -> std::io::Result<bool> {
println!("{}\nType 'yes', other input will be ignore", msg);
let mut buffer = String::new();
io::stdin().read_line(&mut buffer)?;
Ok(buffer.trim() == "yes")
}
fn add_to_backup(content: &String) -> std::io::Result<()> {
let app_dirs = AppDirs::new(Some("Chronos"), AppUI::CommandLine)
.expect("ERROR: Cannot get application data folder");
let path = app_dirs.config_dir.join("data").join("backup");
create_dir_all(&path)?;
let timestamp = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("ERROR: Failed to get timestamp")
.as_secs();
let path = path.join(format!("{}.data.json", timestamp));
let mut file = File::create(path)?;
file.write_all(content.as_bytes())?;
Ok(())
}
fn start_upgrade_from_v1(save: SaveVersion1) -> SaveVersion2 {
println!("INFO: Starting upgrading from version 1 to version 2");
let save = upgrade_to_v2(save);
save
}

27
src/model_version_1.rs Normal file
View File

@@ -0,0 +1,27 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct SaveVersion1 {
pub template: WeekVersion1,
pub version: u32,
pub weeks: Vec<WeekVersion1>,
}
#[derive(Serialize, Deserialize)]
pub struct WeekVersion1 {
pub monday: DayVersion1,
pub tuesday: DayVersion1,
pub wednesday: DayVersion1,
pub thurday: DayVersion1,
pub friday: DayVersion1,
#[serde(alias = "weekNumber", rename(serialize = "weekNumber"))]
pub week_number: u32,
}
#[derive(Serialize, Deserialize)]
pub struct DayVersion1 {
#[serde(alias = "break", rename(serialize = "break"))]
pub break_time: u32,
pub end: String,
pub start: String,
}

151
src/model_version_2.rs Normal file
View File

@@ -0,0 +1,151 @@
pub use crate::model_version_1::DayVersion1;
pub use crate::model_version_1::SaveVersion1;
pub use crate::model_version_1::WeekVersion1;
use chrono::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct SaveVersion2 {
pub template: WeekVersion2,
pub version: u32,
pub weeks: Vec<WeekVersion2>,
}
#[derive(Serialize, Deserialize)]
pub struct WeekVersion2 {
pub monday: DayVersion2,
pub tuesday: DayVersion2,
pub wednesday: DayVersion2,
pub thurday: DayVersion2,
pub friday: DayVersion2,
#[serde(alias = "weekNumber", rename(serialize = "weekNumber"))]
pub week_number: u32,
}
#[derive(Serialize, Deserialize)]
pub struct DayVersion2 {
#[serde(alias = "break", rename(serialize = "break"))]
pub break_time: u32,
pub end: String,
pub start: String,
pub validate: bool,
}
// Upgrade
pub fn upgrade_to_v2(s: SaveVersion1) -> SaveVersion2 {
let save = SaveVersion2 {
version: 2,
template: upgrade_template(s.template),
weeks: upgrade_weeks(s.weeks),
};
save
}
fn upgrade_template(w: WeekVersion1) -> WeekVersion2 {
println!("INFO: Upgrading template");
let week = WeekVersion2 {
monday: upgrade_day(w.monday, false),
tuesday: upgrade_day(w.tuesday, false),
wednesday: upgrade_day(w.wednesday, false),
thurday: upgrade_day(w.thurday, false),
friday: upgrade_day(w.friday, false),
week_number: w.week_number,
};
week
}
fn upgrade_weeks(ws: Vec<WeekVersion1>) -> Vec<WeekVersion2> {
println!("INFO: Upgrading weeks");
let mut v: Vec<WeekVersion2> = Vec::new();
let naive_date_time = Utc::now().naive_utc();
let week_number = naive_date_time.iso_week().week();
let dow = naive_date_time.weekday();
for w in ws {
if w.week_number < week_number {
let week = WeekVersion2 {
monday: upgrade_day(w.monday, true),
tuesday: upgrade_day(w.tuesday, true),
wednesday: upgrade_day(w.wednesday, true),
thurday: upgrade_day(w.thurday, true),
friday: upgrade_day(w.friday, true),
week_number: w.week_number,
};
v.push(week);
} else if w.week_number == week_number {
let week = WeekVersion2 {
monday: upgrade_day(w.monday, dow.num_days_from_monday() > 0),
tuesday: upgrade_day(w.tuesday, dow.num_days_from_monday() > 1),
wednesday: upgrade_day(w.wednesday, dow.num_days_from_monday() > 2),
thurday: upgrade_day(w.thurday, dow.num_days_from_monday() > 3),
friday: upgrade_day(w.friday, dow.num_days_from_monday() > 4),
week_number: w.week_number,
};
v.push(week);
} else {
let week = WeekVersion2 {
monday: upgrade_day(w.monday, false),
tuesday: upgrade_day(w.tuesday, false),
wednesday: upgrade_day(w.wednesday, false),
thurday: upgrade_day(w.thurday, false),
friday: upgrade_day(w.friday, false),
week_number: w.week_number,
};
v.push(week);
}
}
v
}
fn upgrade_day(d: DayVersion1, new_validate: bool) -> DayVersion2 {
println!("INFO: Upgrading day");
let day = DayVersion2 {
break_time: d.break_time,
end: d.end,
start: d.start,
validate: new_validate,
};
day
}
// Downgrade
pub fn downgrade_to_v1(s: SaveVersion2) -> SaveVersion1 {
let save = SaveVersion1 {
version: 1,
template: downgrade_week(s.template),
weeks: downgrade_weeks(s.weeks),
};
save
}
fn downgrade_week(w: WeekVersion2) -> WeekVersion1 {
let week = WeekVersion1 {
monday: downgrade_day(w.monday),
tuesday: downgrade_day(w.tuesday),
wednesday: downgrade_day(w.wednesday),
thurday: downgrade_day(w.thurday),
friday: downgrade_day(w.friday),
week_number: w.week_number,
};
week
}
fn downgrade_weeks(ws: Vec<WeekVersion2>) -> Vec<WeekVersion1> {
println!("INFO: Downgrading weeks");
let mut v: Vec<WeekVersion1> = Vec::new();
for w in ws {
v.push(downgrade_week(w));
}
v
}
fn downgrade_day(d: DayVersion2) -> DayVersion1 {
println!("INFO: Downgrading day");
let day = DayVersion1 {
break_time: d.break_time,
end: d.end,
start: d.start,
};
day
}