use serde::{Deserialize, Serialize};
pub const LAYOUT_DESCRIPTOR_VERSION: u32 = 1;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct CellLayout {
pub addr: String,
pub formula: Option<String>,
pub value: Option<String>,
pub number_format: Option<String>,
pub fill_argb: Option<String>,
pub font_argb: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SheetLayout {
pub name: String,
pub hidden: bool,
pub cells: Vec<CellLayout>,
pub merges: Vec<String>,
pub col_widths: Vec<(u16, f64)>,
pub hidden_cols: Vec<u16>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct LayoutDescriptor {
pub descriptor_version: u32,
pub source_workbook_hash: Option<String>,
pub sheets: Vec<SheetLayout>,
}
#[cfg(test)]
mod tests {
use super::*;
fn sample() -> LayoutDescriptor {
LayoutDescriptor {
descriptor_version: LAYOUT_DESCRIPTOR_VERSION,
source_workbook_hash: Some("a".repeat(64)),
sheets: vec![SheetLayout {
name: "7_Quote".to_string(),
hidden: false,
cells: vec![
CellLayout {
addr: "C11".to_string(),
formula: Some("SUM(C9:C10)".to_string()),
value: Some("1594.93".to_string()),
number_format: Some("#,##0.00".to_string()),
fill_argb: Some("FFE2EFDA".to_string()),
font_argb: None,
},
CellLayout {
addr: "C9".to_string(),
formula: None,
value: Some("532.66".to_string()),
number_format: None,
fill_argb: None,
font_argb: Some("FF0000FF".to_string()),
},
],
merges: vec!["A1:B1".to_string()],
col_widths: vec![(3, 12.5)],
hidden_cols: vec![7],
}],
}
}
#[test]
fn layout_descriptor_round_trips_serialize_deserialize() {
let d = sample();
let json = serde_json::to_string_pretty(&d).expect("serialize");
let back: LayoutDescriptor = serde_json::from_str(&json).expect("deserialize");
assert_eq!(d, back, "LayoutDescriptor round-trips to an equal value");
}
#[test]
fn layout_descriptor_serializes_sheet_name_and_cell_addr_and_format() {
let d = sample();
let json = serde_json::to_string_pretty(&d).expect("serialize");
assert!(json.contains("7_Quote"), "sheet name present: {json}");
assert!(json.contains("C11"), "cell addr present");
assert!(json.contains("SUM(C9:C10)"), "formula present");
assert!(json.contains("#,##0.00"), "number_format present");
}
#[test]
fn layout_descriptor_carries_a_serializing_version_field() {
let d = sample();
let v = serde_json::to_value(&d).expect("to value");
assert_eq!(
v["descriptor_version"], LAYOUT_DESCRIPTOR_VERSION,
"descriptor_version serializes to the version key"
);
let json = serde_json::to_string(&d).expect("serialize");
assert!(
json.contains("descriptor_version"),
"the emitted JSON carries the version key"
);
}
#[test]
fn layout_descriptor_optional_fields_round_trip_when_absent() {
let d = LayoutDescriptor {
descriptor_version: LAYOUT_DESCRIPTOR_VERSION,
source_workbook_hash: None,
sheets: vec![SheetLayout {
name: "1_Inputs".to_string(),
hidden: true,
cells: vec![CellLayout {
addr: "E6".to_string(),
formula: None,
value: None,
number_format: None,
fill_argb: None,
font_argb: None,
}],
merges: vec![],
col_widths: vec![],
hidden_cols: vec![],
}],
};
let json = serde_json::to_string(&d).expect("serialize");
let back: LayoutDescriptor = serde_json::from_str(&json).expect("deserialize");
assert_eq!(d, back);
}
}