Write comment and hyperlink maps with A1 keys so JSON save works (#126)

This commit is contained in:
MarekSpodBiedry
2026-10-10 19:08:53 +02:00
parent 19094e2d62
commit 1d33ea81be
4 changed files with 71 additions and 0 deletions
+17
View File
@@ -1018,3 +1018,20 @@ fn malformed_xlsb_keeps_the_current_workbook() {
assert_eq!(v(&s, "A1"), Value::from("Keep me"));
assert!(s.doc().unwrap().is_dirty());
}
#[test]
fn notes_comments_and_links_survive_a_json_save() {
let mut s = s();
s.execute("cell.set", json!({"cell": "A1", "input": "x"})).unwrap();
s.execute("review.newNote", json!({"cell": "A1", "text": "note", "author": "Example"})).unwrap();
s.execute("review.newComment", json!({"cell": "B2", "text": "comment", "author": "Example"})).unwrap();
s.execute("insert.link", json!({"cell": "C3", "target": "Sheet1!A1", "tooltip": "jump"})).unwrap();
let wb = &s.doc().unwrap().wb;
let bytes = crate::io::save_bytes(wb, "book.json").unwrap();
let (back, _) = crate::io::open_bytes("book.json", &bytes).unwrap();
let (before, after) = (wb.active().unwrap(), back.active().unwrap());
assert_eq!((before.comments.len(), before.hyperlinks.len()), (2, 1));
assert_eq!(after.comments, before.comments);
assert_eq!(after.hyperlinks, before.hyperlinks);
assert_eq!(after.cells, before.cells);
}
+51
View File
@@ -0,0 +1,51 @@
//! Serde helpers for maps keyed by `CellRef`. JSON object keys must be strings, so the keys are
//! written as `A1`-style text.
use std::collections::BTreeMap;
use gridcraft_core::CellRef;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S: Serializer, V: Serialize>(map: &BTreeMap<CellRef, V>, s: S) -> Result<S::Ok, S::Error> {
s.collect_map(map.iter().map(|(k, v)| (k.a1(), v)))
}
pub fn deserialize<'de, D: Deserializer<'de>, V: Deserialize<'de>>(d: D) -> Result<BTreeMap<CellRef, V>, D::Error> {
let raw = BTreeMap::<String, V>::deserialize(d)?;
let mut out = BTreeMap::new();
for (k, v) in raw {
let cell = CellRef::parse(&k).ok_or_else(|| D::Error::custom(format!("invalid cell reference `{k}`")))?;
out.insert(cell, v);
}
Ok(out)
}
#[cfg(test)]
mod tests {
use crate::{Comment, Hyperlink, Sheet};
use gridcraft_core::CellRef;
#[test]
fn comments_and_hyperlinks_survive_json() {
let mut s = Sheet::new("S");
let a1 = CellRef::new(0, 0);
let c3 = CellRef::new(2, 2);
s.comments.insert(
a1,
Comment {
author: "Example".into(),
text: "note".into(),
replies: vec![("B".into(), "r".into())],
threaded: true,
resolved: false,
visible: false,
},
);
s.hyperlinks.insert(c3, Hyperlink { target: "Sheet1!A1".into(), tooltip: Some("jump".into()) });
let json = serde_json::to_string(&s).expect("serialize");
let back: Sheet = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back.comments, s.comments);
assert_eq!(back.hyperlinks, s.hyperlinks);
}
}
+1
View File
@@ -6,6 +6,7 @@
mod b64;
pub mod cell;
mod cellmap;
pub mod features;
pub mod sheet;
pub mod store;
+2
View File
@@ -48,7 +48,9 @@ pub struct Sheet {
pub validations: Vec<Validation>,
pub tables: Vec<Table>,
pub autofilter: Option<AutoFilter>,
#[serde(with = "crate::cellmap")]
pub comments: BTreeMap<CellRef, Comment>,
#[serde(with = "crate::cellmap")]
pub hyperlinks: BTreeMap<CellRef, Hyperlink>,
pub charts: Vec<Chart>,
pub images: Vec<Image>,