Skip to content

Commit ee43c50

Browse files
committed
Fixed bug serializing/deserializing workbooks
1 parent 0b78574 commit ee43c50

File tree

7 files changed

+76
-133
lines changed

7 files changed

+76
-133
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "turing-machine"
3-
version = "1.2.1"
3+
version = "1.2.1-rc2"
44
edition = "2021"
55
authors = ["Marcos Gutiérrez Alonso <[email protected]>"]
66
description = "Turing Machine Simulator"

src/window.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -857,8 +857,11 @@ impl MyApp {
857857
ui.label(t!("lbl.resumed", lang));
858858
}
859859
let b = ui.button(text);
860-
861-
if (b.clicked() || ui.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::Space)))
860+
861+
if (b.clicked()
862+
|| ui.input_mut(|i| {
863+
i.consume_key(egui::Modifiers::NONE, egui::Key::Space)
864+
}))
862865
&& !editor_focused
863866
{
864867
if self.tm.finished() {
@@ -896,19 +899,33 @@ impl eframe::App for MyApp {
896899

897900
ctx.input_mut(|i| {
898901
// Check for keyboard shortcuts
899-
if i.consume_shortcut(&egui::KeyboardShortcut::new(egui::Modifiers::COMMAND, egui::Key::S)){
902+
if i.consume_shortcut(&egui::KeyboardShortcut::new(
903+
egui::Modifiers::COMMAND,
904+
egui::Key::S,
905+
)) {
900906
// Ctrl+S
901907
debug!("Saving...");
902908
self.save_file();
903-
} else if i.consume_shortcut(&egui::KeyboardShortcut::new(egui::Modifiers::COMMAND, egui::Key::O)) {
909+
} else if i.consume_shortcut(&egui::KeyboardShortcut::new(
910+
egui::Modifiers::COMMAND,
911+
egui::Key::O,
912+
)) {
904913
// Ctrl+O
905914
debug!("Opening...");
906915
self.load_file();
907-
} else if i.modifiers.shift && i.consume_shortcut(&egui::KeyboardShortcut::new(egui::Modifiers::CTRL, egui::Key::S)) {
916+
} else if i.modifiers.shift
917+
&& i.consume_shortcut(&egui::KeyboardShortcut::new(
918+
egui::Modifiers::CTRL,
919+
egui::Key::S,
920+
))
921+
{
908922
// Ctrl+Shift+S
909923
debug!("Saving as...");
910924
self.save_file_as();
911-
} else if i.consume_shortcut(&egui::KeyboardShortcut::new(egui::Modifiers::COMMAND, egui::Key::R)) {
925+
} else if i.consume_shortcut(&egui::KeyboardShortcut::new(
926+
egui::Modifiers::COMMAND,
927+
egui::Key::R,
928+
)) {
912929
// Ctrl+R
913930
debug!("Restarting...");
914931
self.tm = self.tm.restart(&self.code).unwrap();

src/windows/workbook/book.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ use serde::{self, Deserialize, Serialize};
33

44
use crate::windows::workbook::raw_data_to_image;
55

6-
use super::{exercise::Exercise, load_workbook, MAX_IMG_SIZE};
6+
use super::{exercise::Exercise, load_workbook, Workbook, MAX_IMG_SIZE};
77

88
#[derive(Serialize, Deserialize)]
99
pub struct BookWindow {
1010
lang: String,
11-
exercises: Vec<(String, Vec<Exercise>)>,
11+
exercises: Workbook,
1212
selected: (usize, usize),
1313
}
1414

1515
impl BookWindow {
1616
pub fn new(lang: &str) -> Self {
17-
let exercises: Vec<(String, Vec<Exercise>)> = vec![
17+
let exercises: Workbook = vec![
1818
(
1919
"Chapter 1".to_string(),
2020
vec![
@@ -96,7 +96,7 @@ impl BookWindow {
9696
});
9797

9898
ui.vertical_centered_justified(|ui| {
99-
if let Some(img) = &self.get_exercise(self.selected).image {
99+
if let Some(img) = self.get_exercise(self.selected).get_cover() {
100100
img.show_max_size(ui, MAX_IMG_SIZE);
101101

102102
// Add expandable empty space
@@ -137,7 +137,7 @@ impl BookWindow {
137137
(active, code)
138138
}
139139

140-
fn get_exercise(&self, i: (usize, usize)) -> &Exercise {
141-
&self.exercises[i.0].1[i.1]
140+
fn get_exercise(&mut self, i: (usize, usize)) -> &mut Exercise {
141+
&mut self.exercises[i.0].1[i.1]
142142
}
143143
}

src/windows/workbook/exercise.rs

Lines changed: 20 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
use eframe::epaint::ColorImage;
22
use egui_extras::RetainedImage;
3-
use serde::{
4-
self,
5-
de::{Error as SerdeError, SeqAccess, Visitor},
6-
ser::SerializeStruct,
7-
Deserialize, Deserializer, Serialize, Serializer,
8-
};
9-
use std::fmt;
3+
use serde::{self, Deserialize, Serialize};
4+
use std::fmt::{self, Debug};
105

6+
#[derive(Serialize, Deserialize)]
117
pub struct Exercise {
8+
#[serde(skip)]
129
pub image: Option<RetainedImage>,
1310
original_image: Option<(usize, usize, Vec<u8>)>,
1411
pub title: String,
@@ -46,106 +43,27 @@ impl Exercise {
4643
img.pixels.iter().map(|p| p.to_array()).flatten().collect(),
4744
));
4845
}
49-
}
50-
51-
impl Serialize for Exercise {
52-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
53-
where
54-
S: Serializer,
55-
{
56-
let mut state = serializer.serialize_struct("Exercise", 4)?;
57-
58-
// Ensure title is valid UTF-8
59-
let title = match String::from_utf8(self.title.clone().into_bytes()) {
60-
Ok(valid_title) => valid_title,
61-
Err(_) => {
62-
// Handle the case where the title is not valid UTF-8, e.g., by replacing invalid sequences with � (U+FFFD)
63-
String::from_utf8_lossy(&self.title.as_bytes()).to_string()
64-
}
65-
};
66-
67-
state.serialize_field("title", &title)?;
68-
if let Some(img) = &self.original_image {
69-
state.serialize_field("original_image", img)?;
70-
}
71-
state.serialize_field("code", &self.code)?;
72-
73-
state.end()
74-
}
75-
}
76-
77-
impl<'de> Deserialize<'de> for Exercise {
78-
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79-
where
80-
D: Deserializer<'de>,
81-
{
82-
struct ExerciseVisitor;
83-
84-
impl<'de> Visitor<'de> for ExerciseVisitor {
85-
type Value = Exercise;
8646

87-
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
88-
formatter.write_str("an Exercise in binary format")
89-
}
90-
91-
fn visit_seq<A>(self, mut seq: A) -> Result<Exercise, A::Error>
92-
where
93-
A: SeqAccess<'de>,
94-
{
95-
let title: String = seq
96-
.next_element()?
97-
.ok_or_else(|| A::Error::invalid_length(0, &self))?;
98-
let original_image: (usize, usize, Vec<u8>) = seq
99-
.next_element()?
100-
.ok_or_else(|| A::Error::invalid_length(1, &self))?;
101-
let code: String = seq
102-
.next_element()?
103-
.ok_or_else(|| A::Error::invalid_length(2, &self))?;
104-
105-
Ok(Exercise::new(
106-
&title,
107-
Some(ColorImage::from_rgba_unmultiplied(
108-
[original_image.0 as usize, original_image.1 as usize],
109-
&original_image.2,
110-
)),
111-
code,
112-
))
47+
pub fn get_cover(&mut self) -> Option<&RetainedImage> {
48+
match self.image {
49+
Some(ref img) => Some(img),
50+
None => {
51+
if let Some(img) = &self.original_image {
52+
self.set_cover(ColorImage::from_rgba_premultiplied([img.0, img.1], &img.2));
53+
self.image.as_ref()
54+
} else {
55+
None
56+
}
11357
}
11458
}
115-
116-
deserializer.deserialize_tuple(3, ExerciseVisitor)
11759
}
11860
}
11961

120-
// Custom deserializer for the tuple (String, Vec<Exercise>)
121-
#[allow(dead_code)]
122-
fn deserialize_tuple<'de, D>(deserializer: D) -> Result<(String, Vec<Exercise>), D::Error>
123-
where
124-
D: Deserializer<'de>,
125-
{
126-
struct TupleVisitor;
127-
128-
impl<'de> Visitor<'de> for TupleVisitor {
129-
type Value = (String, Vec<Exercise>);
130-
131-
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
132-
formatter.write_str("a tuple (String, Vec<Exercise>) in binary format")
133-
}
134-
135-
fn visit_seq<A>(self, mut seq: A) -> Result<(String, Vec<Exercise>), A::Error>
136-
where
137-
A: SeqAccess<'de>,
138-
{
139-
let key: String = seq
140-
.next_element()?
141-
.ok_or_else(|| A::Error::invalid_length(0, &self))?;
142-
let value: Vec<Exercise> = seq
143-
.next_element()?
144-
.ok_or_else(|| A::Error::invalid_length(1, &self))?;
145-
146-
Ok((key, value))
147-
}
62+
impl Debug for Exercise {
63+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64+
f.debug_struct("Exercise")
65+
.field("title", &self.title)
66+
.field("code", &self.code)
67+
.finish()
14868
}
149-
150-
deserializer.deserialize_tuple(2, TupleVisitor)
15169
}

src/windows/workbook/mod.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,20 @@ use eframe::egui;
1212
use self::exercise::Exercise;
1313
use eframe::epaint::ColorImage;
1414

15+
type WorkbookChapter = (String, Vec<Exercise>);
16+
type Workbook = Vec<WorkbookChapter>;
17+
1518
#[cfg(not(target_arch = "wasm32"))]
1619
use {
20+
log::{debug, error},
1721
rfd,
1822
std::{fs::File, io::Write, path::PathBuf},
19-
log::{debug, error}
2023
};
2124

2225
#[cfg(target_arch = "wasm32")]
2326
use {
24-
js_sys, web_sys, wasm_bindgen::prelude::*, wasm_bindgen::JsCast,
25-
base64::engine::general_purpose::STANDARD_NO_PAD as base64,
27+
base64::engine::general_purpose::STANDARD_NO_PAD as base64, js_sys, wasm_bindgen::prelude::*,
28+
wasm_bindgen::JsCast, web_sys,
2629
};
2730

2831
const MAX_IMG_SIZE: egui::Vec2 = egui::Vec2::new(600.0, 250.0);
@@ -70,7 +73,7 @@ fn load_image() -> Option<ColorImage> {
7073
{
7174
/*
7275
FIXME: Not working
73-
76+
7477
let window = web_sys::window().expect("Failed to get window");
7578
let document = window.document().expect("Failed to get document");
7679
let input = document
@@ -173,12 +176,12 @@ fn image_to_raw_data(color_image: &ColorImage) -> (usize, usize, Vec<u8>) {
173176
(size[0], size[1], raw_data.to_vec())
174177
}
175178

176-
pub fn save_workbook(exercises: &Vec<(String, Vec<Exercise>)>) {
179+
pub fn save_workbook(exercises: &Workbook) {
177180
#[cfg(target_arch = "wasm32")]
178181
{
179182
/*
180183
FIXME: Not working
181-
184+
182185
let data = bincode::serialize(&exercises).unwrap();
183186
let data_url = format!(
184187
"data:application/octet-stream;base64,{}",
@@ -202,19 +205,22 @@ pub fn save_workbook(exercises: &Vec<(String, Vec<Exercise>)>) {
202205
let data = bincode::serialize(&exercises).unwrap();
203206
let mut file = File::create(&f).unwrap();
204207
file.write_all(&data).unwrap();
208+
205209
debug!("Workbook saved at {:?}", f);
210+
211+
drop(file);
206212
} else {
207213
error!("Cannot save workbook");
208214
}
209215
}
210216
}
211217

212-
pub fn load_workbook() -> Option<Vec<(String, Vec<Exercise>)>> {
218+
pub fn load_workbook() -> Option<Workbook> {
213219
#[cfg(target_arch = "wasm32")]
214220
{
215221
/*
216222
FIXME: Not working
217-
223+
218224
let window = web_sys::window().unwrap();
219225
let document = window.document().unwrap();
220226
let input = document
@@ -248,19 +254,19 @@ pub fn load_workbook() -> Option<Vec<(String, Vec<Exercise>)>> {
248254
let file_path = rfd::FileDialog::new()
249255
.add_filter("TuringMachine Workbook", &["wb"])
250256
.set_directory(&path)
251-
.pick_files();
257+
.pick_file();
252258

253259
match file_path {
254260
Some(f) => {
255-
let mut file = File::open(&f[0]).expect("File not found");
261+
let mut file = File::open(&f).expect("File not found");
256262
let mut reader: Vec<u8> = Vec::new();
257263
file.read_to_end(&mut reader).expect("Could not read file");
258264

265+
debug!("Read {} bytes", reader.len());
259266

260-
261-
match bincode::deserialize(&reader) {
267+
match bincode::deserialize::<Workbook>(&reader) {
262268
Ok(exercises) => {
263-
debug!("Workbook loaded from {:?}", f[0]);
269+
debug!("Workbook loaded from {:?}", &f);
264270
Some(exercises)
265271
}
266272
Err(e) => {

src/windows/workbook/wb_editor.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
use eframe::egui;
22

3-
use super::{exercise::Exercise, load_image, save_workbook, MAX_IMG_SIZE};
3+
use super::{
4+
exercise::Exercise, load_image, save_workbook, Workbook, WorkbookChapter, MAX_IMG_SIZE,
5+
};
46

57
pub struct WorkbookEditorWindow {
68
lang: String,
7-
chapters: Vec<(String, Vec<Exercise>)>,
9+
chapters: Workbook,
810
selected: (usize, usize),
911
}
1012

1113
impl WorkbookEditorWindow {
1214
pub fn new(lang: &str) -> Self {
13-
let exercises: Vec<(String, Vec<Exercise>)> = vec![];
15+
let exercises: Workbook = vec![];
1416

1517
Self {
1618
lang: String::from(lang),
@@ -167,7 +169,7 @@ impl WorkbookEditorWindow {
167169
active
168170
}
169171

170-
fn new_chapter(chapters_len: usize, exercises_len: usize) -> (String, Vec<Exercise>) {
172+
fn new_chapter(chapters_len: usize, exercises_len: usize) -> WorkbookChapter {
171173
(
172174
format!("New Chapter {}", chapters_len + 1),
173175
vec![WorkbookEditorWindow::new_exercise(exercises_len)],

0 commit comments

Comments
 (0)