diff --git a/Cargo.lock b/Cargo.lock index 0adc0fd..8b0da48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -162,6 +162,38 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "3.0.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", + "strsim", + "termcolor", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clap_derive" +version = "3.0.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "cmake" version = "0.1.45" @@ -852,6 +884,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + [[package]] name = "heck" version = "0.3.2" @@ -884,6 +922,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "indexmap" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.9" @@ -1034,6 +1082,12 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" +[[package]] +name = "os_str_bytes" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85" + [[package]] name = "pango" version = "0.9.1" @@ -1343,6 +1397,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bare" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01db2255aa98fb93ad74272d8b2e6fd4851860e733e944b9439cf148127164b2" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.117" @@ -1487,6 +1550,7 @@ checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" name = "stiletto" version = "0.1.0" dependencies = [ + "clap", "druid", "gdk", "gio", @@ -1494,9 +1558,16 @@ dependencies = [ "im", "log", "serde", + "serde_bare", "serde_json", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strum" version = "0.18.0" @@ -1551,6 +1622,24 @@ dependencies = [ "version-compare", ] +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789" +dependencies = [ + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.24" @@ -1759,6 +1848,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + [[package]] name = "unicode-xid" version = "0.2.1" @@ -1807,6 +1902,12 @@ dependencies = [ "getrandom", ] +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version-compare" version = "0.0.10" @@ -1905,6 +2006,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 16ac91b..817de97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,18 +3,16 @@ name = "stiletto" version = "0.1.0" authors = ["Enrico Lumetti "] edition = "2018" +default-run = "stiletto" [dependencies] log = "0.4" druid = { version = "0.7.0", features = ["im", "svg"] } im = { version = "*" } serde = { version = "1.0", features = ["derive"] } -#serde_bare = "0.3.0" +serde_bare = "0.3.0" serde_json = "1.0" - -[patch.crates-io] -druid = { git = "https://github.com/doppioandante/druid", branch = "v0.7.0_stiletto", features = ["im", "svg"] } -#druid = { path = "../druid/druid/", features = ["im", "svg"] } +clap = "3.0.0-beta.2" [target.'cfg(target_os="linux")'.dependencies.gtk] version = "0.9.2" @@ -27,3 +25,9 @@ features = ["v2_56"] [target.'cfg(target_os="linux")'.dependencies.gdk] version = "0.13.2" features = ["v3_22"] + + +[patch.crates-io] +druid = { git = "https://github.com/doppioandante/druid", branch = "v0.7.0_stiletto", features = ["im", "svg"] } +#druid = { path = "../druid/druid/", features = ["im", "svg"] } + diff --git a/src/bin/migrate.rs b/src/bin/migrate.rs new file mode 100644 index 0000000..e3694f6 --- /dev/null +++ b/src/bin/migrate.rs @@ -0,0 +1,48 @@ +// Stiletto +// Copyright (C) 2020 Stiletto Authors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use clap::{Arg, App}; +use stiletto::migration::open_stiletto_document; + +use std::fs::File; +use std::path::PathBuf; + +fn main() { + let matches = App::new("Stiletto Migration CLI") + .version("0.1.0") + .author("Stiletto Authors") + .about("Migrate a stlt file to the latest version") + .arg(Arg::new("INPUT") + .about("Sets the input file to use") + .required(true) + .index(1)) + .arg(Arg::new("OUTPUT") + .about("Sets the output file to use") + .required(true) + .index(2)) + .get_matches(); + + let input_path = matches.value_of("INPUT").unwrap(); + let output_path = matches.value_of("OUTPUT").unwrap(); + + let out_file = File::create(&output_path).expect("Cannot create output fle"); + + let result = open_stiletto_document(&PathBuf::from(&input_path)); + if let Ok(document) = result { + let snapshot = document.migrate_to_latest(); + snapshot.to_writer(out_file); + } +} diff --git a/src/lib.rs b/src/lib.rs index 222ad17..25ddcfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ pub mod canvas; pub mod history; pub mod tool; pub mod widget; +pub mod migration; pub mod commands { use druid::Selector; @@ -30,9 +31,18 @@ pub mod commands { pub const POP_ERASER: Selector<()> = Selector::new("pop_eraser_context"); } +pub const STILETTO_FORMAT_MAJOR: u16 = 0; +pub const STILETTO_FORMAT_MINOR: u16 = 1; + #[derive(Serialize, Deserialize, Debug)] pub struct DocumentSnapshot { pub format_version_major: u16, pub format_version_minor: u16, pub canvas_elements: Vec, } + +impl DocumentSnapshot { + pub fn to_writer(&self, writer: W) -> Result<(), serde_json::Error> { + serde_json::to_writer_pretty(writer, &self) + } +} diff --git a/src/main.rs b/src/main.rs index 2ba5bf9..3cefc60 100644 --- a/src/main.rs +++ b/src/main.rs @@ -260,7 +260,7 @@ impl AppDelegate for Delegate { let res_file = File::create(&path_buf); if let Ok(f) = res_file { let write_res = - serde_json::to_writer_pretty(f, &data.canvas.get_document_snapshot()); + data.canvas.get_document_snapshot().to_writer(f); if write_res.is_err() { warn!("Error while saving: {:?}", write_res.err()); } else { diff --git a/src/migration.rs b/src/migration.rs new file mode 100644 index 0000000..ac51248 --- /dev/null +++ b/src/migration.rs @@ -0,0 +1,142 @@ +// Stiletto +// Copyright (C) 2020 Stiletto Authors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use crate::{DocumentSnapshot, STILETTO_FORMAT_MAJOR, STILETTO_FORMAT_MINOR}; + +use std::error::Error; +use std::fmt; +use std::fs::File; +use std::io; +use std::path::PathBuf; + +#[derive(Debug)] +pub enum MigrationError { + SerdeJsonError(serde_json::Error), + SerdeBareError(serde_bare::Error), + IoError(io::Error), + + UnexpectedVersion(u16, u16, u16, u16), +} + +#[derive(Debug)] +pub enum StilettoDocument { + DocumentSnapshot0_1(DocumentSnapshot), + DocumentSnapshot0_2(DocumentSnapshot), +} + +impl StilettoDocument { + pub fn version(&self) -> (u16, u16) { + match &self { + StilettoDocument::DocumentSnapshot0_1(_) => (0, 1), + StilettoDocument::DocumentSnapshot0_2(_) => (0, 2), + } + } + + pub fn migrate_to_next(self) -> StilettoDocument { + match self { + StilettoDocument::DocumentSnapshot0_1(_) => self, + StilettoDocument::DocumentSnapshot0_2(_) => self, + } + } + + pub fn migrate_to_latest(mut self) -> DocumentSnapshot { + while self.version() != (STILETTO_FORMAT_MAJOR, STILETTO_FORMAT_MINOR) { + self = self.migrate_to_next() + } + + assert!(matches!(self, StilettoDocument::DocumentSnapshot0_2(_))); + match self { + StilettoDocument::DocumentSnapshot0_2(snapshot) => snapshot, + _ => panic!("Wrong Document Snapshot Version") + } + } +} + + +impl fmt::Display for MigrationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self { + MigrationError::SerdeJsonError(e) => e.fmt(f), + MigrationError::SerdeBareError(e) => e.fmt(f), + MigrationError::IoError(e) => e.fmt(f), + MigrationError::UnexpectedVersion(major, minor, e_major, e_minor) => { + write!( + f, "Unexpected version ({}, {}): was expecting ({}, {})", + major, minor, e_major, e_minor + ) + }, + } + } +} + +impl Error for MigrationError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + MigrationError::SerdeJsonError(e) => e.source(), + MigrationError::SerdeBareError(e) => e.source(), + MigrationError::IoError(e) => e.source(), + MigrationError::UnexpectedVersion(..) => None, + } + } +} + +impl From for MigrationError { + fn from(error: io::Error) -> Self { + MigrationError::IoError(error) + } +} + +impl From for MigrationError { + fn from(error: serde_json::Error) -> Self { + MigrationError::SerdeJsonError(error) + } +} + +impl From for MigrationError { + fn from(error: serde_bare::Error) -> Self { + MigrationError::SerdeBareError(error) + } +} + +fn open_0_1(path: &PathBuf) -> Result { + let f = File::open(path)?; + let document_snapshot: DocumentSnapshot = serde_json::from_reader(f)?; + if document_snapshot.format_version_major != 0 || + document_snapshot.format_version_minor != 1 { + Err(MigrationError::UnexpectedVersion( + document_snapshot.format_version_major, + document_snapshot.format_version_minor, + 0, 1 + )) + } else { + Ok(StilettoDocument::DocumentSnapshot0_1(document_snapshot)) + } +} + +fn open_0_2(path: &PathBuf) -> Result { + let f = File::open(path)?; + let document_snapshot: DocumentSnapshot = serde_bare::from_reader(f)?; + Ok(StilettoDocument::DocumentSnapshot0_2(document_snapshot)) +} + +pub fn open_stiletto_document(path: &PathBuf) -> Result { + if let Ok(res) = open_0_1(path) { + return Ok(res); + } + + open_0_2(path) +} +