- 1
//! The paged projection the views draw (docs/design/72, P4) and the - 2
//! Structure listing (U4). - 3
- 4
#![allow(clippy::unwrap_used, clippy::expect_used)] - 5
- 6
use std::io::Cursor; - 7
- 8
use vak_ooxml::projection::{self, PAGE_BYTES}; - 9
use vak_ooxml::{Limits, fixtures, read}; - 10
- 11
fn read_bytes(bytes: &[u8]) -> read::Document { - 12
read::read(Cursor::new(bytes.to_vec()), Limits::default()).unwrap() - 13
} - 14
- 15
#[test] - 16
fn pages_cover_every_unit_once_in_order() { - 17
let document = read_bytes(&fixtures::docx()); - 18
let mut from = 0; - 19
let mut anchors = Vec::new(); - 20
let mut pages = 0; - 21
loop { - 22
let page = projection::project(&document, from, 200); - 23
assert_eq!(page.from, from); - 24
assert!(!page.units.is_empty(), "a page always holds a unit"); - 25
anchors.extend(page.units.iter().map(|unit| unit.anchor.clone())); - 26
pages += 1; - 27
match page.next { - 28
Some(next) => from = next, - 29
None => break, - 30
} - 31
} - 32
assert!(pages > 1, "a small budget pages the document"); - 33
let all: Vec<String> = document - 34
.units - 35
.iter() - 36
.map(|unit| unit.anchor.clone()) - 37
.collect(); - 38
assert_eq!(anchors, all); - 39
- 40
let whole = projection::project(&document, 0, PAGE_BYTES); - 41
assert_eq!(whole.next, None); - 42
assert_eq!(whole.total_units, document.units.len()); - 43
assert_eq!(whole.kind, "Word document"); - 44
assert_eq!(whole.extension, "docx"); - 45
for entry in &whole.outline { - 46
let first = &document.units[entry.first_unit]; - 47
assert!( - 48
entry.units == 0 || first.anchor.starts_with('p') || first.anchor == entry.anchor, - 49
"{entry:?} points at {first:?}" - 50
); - 51
} - 52
- 53
let labelled = projection::project( - 54
&read_bytes(&fixtures::signed_labelled_docx()), - 55
0, - 56
PAGE_BYTES, - 57
); - 58
assert!( - 59
labelled - 60
.flags - 61
.iter() - 62
.any(|flag| flag.contains("digitally signed")), - 63
"{:?}", - 64
labelled.flags - 65
); - 66
assert_eq!(labelled.sensitivity_labels, ["Confidential"]); - 67
} - 68
- 69
#[test] - 70
fn a_sheet_page_carries_its_cells_and_a_slide_deck_its_outline() { - 71
let workbook = projection::project(&read_bytes(&fixtures::xlsx()), 0, PAGE_BYTES); - 72
assert_eq!(workbook.kind, "Excel workbook"); - 73
assert!(workbook.outline.iter().any(|entry| entry.title == "Budget")); - 74
let row = workbook - 75
.units - 76
.iter() - 77
.find(|unit| unit.cells.iter().any(|(address, _)| address == "B2")) - 78
.expect("row 2"); - 79
assert!(row.anchor.starts_with("Budget!")); - 80
- 81
let deck = projection::project(&read_bytes(&fixtures::pptx()), 0, PAGE_BYTES); - 82
assert_eq!(deck.kind, "PowerPoint presentation"); - 83
assert!( - 84
deck.outline - 85
.iter() - 86
.all(|entry| entry.anchor.starts_with("slide:")) - 87
); - 88
assert!(!deck.outline.is_empty()); - 89
} - 90
- 91
#[test] - 92
fn a_unit_larger_than_the_page_is_cut_and_says_so() { - 93
let long = "Long paragraph text. ".repeat(400); - 94
let body = fixtures::MINIMAL_WORD_BODY.replace("Hello", &long); - 95
let bytes = fixtures::word_with(fixtures::WORD_MAIN, &body, &[], &[], &[], &[]); - 96
let page = projection::project(&read_bytes(&bytes), 0, 2048); - 97
assert_eq!(page.units.len(), 1); - 98
assert!(page.units[0].text.len() < long.len()); - 99
assert!( - 100
page.units[0] - 101
.labels - 102
.iter() - 103
.any(|label| label.starts_with("shown up to")), - 104
"{:?}", - 105
page.units[0].labels - 106
); - 107
} - 108
- 109
#[test] - 110
fn structure_lists_parts_types_and_relationships() { - 111
let rels = r#"<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId9" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="https://example.com/" TargetMode="External"/></Relationships>"#; - 112
let bytes = fixtures::with_parts( - 113
&fixtures::docx(), - 114
&[("word/_rels/document.xml.rels", rels.as_bytes())], - 115
); - 116
let structure = projection::structure(Cursor::new(bytes), Limits::default()).unwrap(); - 117
assert_eq!(structure.main_part, "word/document.xml"); - 118
let main = structure - 119
.parts - 120
.iter() - 121
.find(|part| part.name == "word/document.xml") - 122
.unwrap(); - 123
assert!( - 124
main.content_type - 125
.as_deref() - 126
.is_some_and(|kind| kind.contains("wordprocessingml.document.main")) - 127
); - 128
assert!(main.size > 0); - 129
assert!( - 130
structure - 131
.relationships - 132
.iter() - 133
.any(|relationship| relationship.source.is_empty() - 134
&& relationship.kind == "officeDocument" - 135
&& relationship.target == "word/document.xml") - 136
); - 137
assert!( - 138
structure - 139
.relationships - 140
.iter() - 141
.any(|relationship| relationship.external - 142
&& relationship.kind == "hyperlink" - 143
&& relationship.source == "word/document.xml"), - 144
"{:?}", - 145
structure.relationships - 146
); - 147
assert!(structure.untyped_parts.is_empty()); - 148
} - 149
- 150
#[test] - 151
fn a_citation_finds_the_unit_it_names() { - 152
let workbook = read_bytes(&fixtures::xlsx()); - 153
let rows: Vec<(usize, &read::Unit)> = workbook - 154
.units - 155
.iter() - 156
.enumerate() - 157
.filter(|(_, unit)| unit.kind == read::UnitKind::SheetRow) - 158
.collect(); - 159
let (index, row) = rows[1]; - 160
let (cell, _) = &row.cells[1]; - 161
let sheet = row.anchor.split_once('!').unwrap().0; - 162
assert_eq!( - 163
projection::locate(&workbook, &row.anchor), - 164
Some(index), - 165
"the row by its own anchor" - 166
); - 167
assert_eq!( - 168
projection::locate(&workbook, &format!("{sheet}!{cell}")), - 169
Some(index), - 170
"a cell finds its row" - 171
); - 172
assert_eq!( - 173
projection::locate(&workbook, &format!("{sheet}!{}:Z99", cell.to_lowercase())), - 174
Some(index), - 175
"a range finds the row of its first cell" - 176
); - 177
assert_eq!( - 178
projection::locate(&workbook, &format!("{sheet}!ZZ9999")), - 179
None - 180
); - 181
- 182
let deck = read_bytes(&fixtures::pptx()); - 183
let slide = deck - 184
.units - 185
.iter() - 186
.position(|unit| unit.kind == read::UnitKind::Shape) - 187
.unwrap(); - 188
let slide_anchor = deck.units[slide].anchor.split('/').next().unwrap(); - 189
let located = projection::locate(&deck, slide_anchor).unwrap(); - 190
assert!(deck.units[located].anchor.starts_with(slide_anchor)); - 191
assert!(located <= slide); - 192
} - 193
- 194
#[test] - 195
fn a_cited_page_opens_at_the_start_of_its_section() { - 196
let workbook = read_bytes(&fixtures::xlsx()); - 197
let rows: Vec<usize> = workbook - 198
.units - 199
.iter() - 200
.enumerate() - 201
.filter(|(_, unit)| unit.kind == read::UnitKind::SheetRow) - 202
.map(|(index, _)| index) - 203
.collect(); - 204
let cited = rows[1]; - 205
let sheet = workbook - 206
.sections - 207
.iter() - 208
.find(|section| section.units.contains(&cited)) - 209
.unwrap(); - 210
assert_eq!( - 211
projection::page_start(&workbook, cited, PAGE_BYTES), - 212
sheet.units.start, - 213
"a cited row opens with its sheet's first row, the header" - 214
); - 215
assert_eq!( - 216
projection::page_start(&workbook, cited, 1), - 217
cited, - 218
"when the section does not fit, the page starts at the citation" - 219
); - 220
} - 221
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.