From 21d9a41d8f187a3b1e8f4cea9d077c51e4de12cd Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 1 Oct 2024 10:35:22 +0200 Subject: [PATCH] Fix `Tree` serialization (#87) --- Cargo.lock | 26 ++++++++++++++++++++++++ Cargo.toml | 6 +++++- src/tree.rs | 30 ++++++++++++++++++++++++++-- tests/serialize.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 tests/serialize.rs diff --git a/Cargo.lock b/Cargo.lock index 1ff2600..9ecd3fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -485,7 +485,9 @@ dependencies = [ "env_logger", "itertools", "log", + "ron", "serde", + "serde_json", ] [[package]] @@ -831,6 +833,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "jni" version = "0.21.1" @@ -1482,6 +1490,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "same-file" version = "1.0.6" @@ -1523,6 +1537,18 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "simd-adler32" version = "0.3.7" diff --git a/Cargo.toml b/Cargo.toml index ec56181..ad43e79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,8 +36,8 @@ log = { version = "0.4", features = ["std"] } serde = { version = "1", features = ["derive"], optional = true } -# For the example: [dev-dependencies] +# For the example: eframe = { version = "0.29", default-features = false, features = [ "default_fonts", "glow", @@ -48,6 +48,10 @@ env_logger = { version = "0.10", default-features = false, features = [ "humantime", ] } +# For tests: +serde_json = "1" +ron = "0.8" + [patch.crates-io] diff --git a/src/tree.rs b/src/tree.rs index df9c28a..1511dbb 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -41,6 +41,7 @@ pub struct Tree { /// When finite, this values contains the exact height of this tree #[cfg_attr( feature = "serde", + serde(serialize_with = "serialize_f32_infinity_as_null"), serde(deserialize_with = "deserialize_f32_null_as_infinity") )] height: f32, @@ -48,11 +49,25 @@ pub struct Tree { /// When finite, this values contains the exact width of this tree #[cfg_attr( feature = "serde", + serde(serialize_with = "serialize_f32_infinity_as_null"), serde(deserialize_with = "deserialize_f32_null_as_infinity") )] width: f32, } +// Workaround for JSON which doesn't support infinity, because JSON is stupid. +#[cfg(feature = "serde")] +fn serialize_f32_infinity_as_null( + t: &f32, + serializer: S, +) -> Result { + if t.is_infinite() { + serializer.serialize_none() + } else { + serializer.serialize_some(t) + } +} + #[cfg(feature = "serde")] fn deserialize_f32_null_as_infinity<'de, D: serde::Deserializer<'de>>( des: D, @@ -95,9 +110,20 @@ impl std::fmt::Debug for Tree { } } - if let Some(root) = self.root { + let Self { + id, + root, + tiles, + width, + height, + } = self; + + if let Some(root) = root { writeln!(f, "Tree {{")?; - format_tile(f, &self.tiles, 1, root)?; + writeln!(f, " id: {id:?}")?; + writeln!(f, " width: {width:?}")?; + writeln!(f, " height: {height:?}")?; + format_tile(f, tiles, 1, *root)?; write!(f, "}}") } else { writeln!(f, "Tree {{ }}") diff --git a/tests/serialize.rs b/tests/serialize.rs new file mode 100644 index 0000000..02899ca --- /dev/null +++ b/tests/serialize.rs @@ -0,0 +1,50 @@ +#![cfg(feature = "serde")] + +use egui_tiles::{Tiles, Tree}; + +#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] +struct Pane { + nr: usize, +} + +fn create_tree() -> Tree { + let mut next_view_nr = 0; + let mut gen_pane = || { + let pane = Pane { nr: next_view_nr }; + next_view_nr += 1; + pane + }; + + let mut tiles = Tiles::default(); + + let mut tabs = vec![]; + tabs.push({ + let children = (0..7).map(|_| tiles.insert_pane(gen_pane())).collect(); + tiles.insert_horizontal_tile(children) + }); + tabs.push({ + let cells = (0..11).map(|_| tiles.insert_pane(gen_pane())).collect(); + tiles.insert_grid_tile(cells) + }); + tabs.push(tiles.insert_pane(gen_pane())); + + let root = tiles.insert_tab_tile(tabs); + + Tree::new("my_tree", root, tiles) +} + +#[test] +fn test_serialize_json() { + let original = create_tree(); + let json = serde_json::to_string(&original).expect("json serialize"); + let restored = serde_json::from_str(&json).expect("json deserialize"); + assert_eq!(original, restored, "JSON did not round-trip"); +} + +#[test] +fn test_serialize_ron() { + let original = create_tree(); + let ron = ron::to_string(&original).expect("ron serialize"); + let restored = ron::from_str(&ron).expect("ron deserialize"); + assert_eq!(original, restored, "RON did not round-trip"); +}