Skip to main content

ravenrs_sheet/model/
preserved.rs

1//! File content GridCraft does not model, carried through unchanged so that saving a workbook
2//! never silently drops it (XLSX pass-through: custom XML, external links, connections, query
3//! tables, form controls, OLE objects…).
4//!
5//! The data is opaque to everything except the file-format crate: the model only holds it, so a
6//! sheet's kept content disappears with the sheet. It is not part of the JSON format.
7
8use std::sync::Arc;
9
10use crate::model::sheet::Visibility;
11
12/// A relationship from an owning part (the package root, the workbook or a sheet, or a kept part)
13/// to a kept part or to an external target.
14#[derive(Clone, Debug, Default, PartialEq, Eq)]
15pub struct PreservedRel {
16    /// The id in the source file (`rId7`). Ids of an owner GridCraft writes are reassigned on save.
17    pub id: String,
18    /// The full relationship type URI.
19    pub rel_type: String,
20    /// The original part name of the target (no leading `/`), or the raw target of an external
21    /// relationship. External targets are never opened.
22    pub target: String,
23    pub external: bool,
24}
25
26/// A kept package part.
27#[derive(Clone, Debug, Default, PartialEq, Eq)]
28pub struct PreservedPart {
29    /// Part name in the source file (no leading `/`).
30    pub name: String,
31    pub content_type: String,
32    pub data: Arc<[u8]>,
33    /// The part's own relationships, ids unchanged (the part's bytes refer to them).
34    pub rels: Vec<PreservedRel>,
35}
36
37/// A raw XML element of an owning part that refers to kept parts, re-emitted on save with its
38/// relationship ids rewritten, or that Excel needs back unchanged (the workbook's `fileVersion`).
39#[derive(Clone, Debug, Default, PartialEq, Eq)]
40pub struct PreservedXml {
41    /// Which slot of the owner it belongs in (`externalReferences`, `fileVersion`, `ext`,
42    /// `oleObjects`, `controls`).
43    pub slot: String,
44    /// The element, namespace declarations it needs included.
45    pub xml: String,
46}
47
48/// A chart sheet (a sheet that holds one chart and no cells) kept as it was in the source file. Its
49/// part, drawing, chart, style and colour parts are in [`Preserved::parts`]; the workbook
50/// relationship to it is in [`Preserved::rels`].
51#[derive(Clone, Debug, Default, PartialEq, Eq)]
52pub struct PreservedSheet {
53    pub name: String,
54    pub visibility: Visibility,
55    /// The id of the workbook relationship to the chart sheet part, in the source file.
56    pub rel_id: String,
57    /// How many worksheets come before it: its position among the model's sheets.
58    pub before: usize,
59    /// Its `sheetId` in the source file.
60    pub sheet_id: u32,
61}
62
63/// Unmodelled content kept for one owner (the workbook, which also holds package-root content,
64/// or one sheet).
65#[derive(Clone, Debug, Default, PartialEq, Eq)]
66pub struct Preserved {
67    /// Relationships of the package root (`_rels/.rels`) to kept parts (workbook owner only).
68    pub package_rels: Vec<PreservedRel>,
69    /// Relationships of the owning workbook or sheet part to kept parts.
70    pub rels: Vec<PreservedRel>,
71    /// Every kept part reachable from `package_rels` and `rels`.
72    pub parts: Vec<PreservedPart>,
73    /// Owner XML elements that refer to kept parts.
74    pub elements: Vec<PreservedXml>,
75    /// The sheet's legacy VML drawing when kept elements need its shapes (form controls, OLE
76    /// objects). Its own relationships point into `parts`.
77    pub vml: Option<PreservedPart>,
78    /// Chart sheets, in file order (workbook owner only).
79    pub sheets: Vec<PreservedSheet>,
80}
81
82impl Preserved {
83    pub fn is_empty(&self) -> bool {
84        self.package_rels.is_empty()
85            && self.rels.is_empty()
86            && self.parts.is_empty()
87            && self.elements.is_empty()
88            && self.vml.is_none()
89            && self.sheets.is_empty()
90    }
91    /// Total bytes held.
92    pub fn byte_len(&self) -> usize {
93        self.parts.iter().chain(self.vml.iter()).fold(0usize, |n, p| n.saturating_add(p.data.len()))
94    }
95}