1use std::collections::BTreeMap;
4use std::sync::Arc;
5use std::sync::atomic::{AtomicU64, Ordering};
6
7use crate::base::{CellRef, RangeRef, Value};
8use serde::{Deserialize, Serialize};
9
10use crate::model::cell::{Cell, CellPicture};
11use crate::model::features::{
12 Anchor, AutoFilter, Chart, Comment, CondFormat, FormControl, Hyperlink, Image, LineInfo, ObjKind, ObjRef, PivotTable, PrintSettings, Shape,
13 ShapeKind, SheetProtection, Slicer, Sparkline, Table, Validation,
14};
15use crate::model::store::CellStore;
16use crate::model::style::{Color, StyleId};
17
18#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
19pub enum Visibility {
20 #[default]
21 Visible,
22 Hidden,
23 VeryHidden,
24}
25
26#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
28#[serde(rename_all = "camelCase")]
29pub enum SheetViewMode {
30 #[default]
31 Normal,
32 PageLayout,
34 PageBreakPreview,
35}
36
37impl SheetViewMode {
38 pub fn as_str(self) -> &'static str {
40 match self {
41 SheetViewMode::Normal => "normal",
42 SheetViewMode::PageLayout => "pageLayout",
43 SheetViewMode::PageBreakPreview => "pageBreakPreview",
44 }
45 }
46 pub fn parse(s: &str) -> Option<SheetViewMode> {
47 match s {
48 "normal" => Some(SheetViewMode::Normal),
49 "pageLayout" => Some(SheetViewMode::PageLayout),
50 "pageBreakPreview" => Some(SheetViewMode::PageBreakPreview),
51 _ => None,
52 }
53 }
54}
55
56#[derive(Clone, Copy, Debug)]
62pub struct SheetUid(u64);
63
64static NEXT_SHEET_UID: AtomicU64 = AtomicU64::new(1);
66
67impl SheetUid {
68 pub fn fresh() -> SheetUid {
69 SheetUid(NEXT_SHEET_UID.fetch_add(1, Ordering::Relaxed))
70 }
71 pub fn get(self) -> u64 {
72 self.0
73 }
74}
75
76impl Default for SheetUid {
77 fn default() -> SheetUid {
78 SheetUid::fresh()
79 }
80}
81
82impl PartialEq for SheetUid {
83 fn eq(&self, _: &SheetUid) -> bool {
84 true
85 }
86}
87
88#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
89pub struct Sheet {
90 pub name: String,
91 #[serde(skip)]
93 pub uid: SheetUid,
94 #[serde(skip)]
97 pub file_id: u32,
98 pub cells: CellStore,
99 #[serde(default, skip_serializing_if = "BTreeMap::is_empty", with = "picture_map")]
101 pub cell_pictures: BTreeMap<CellRef, Arc<CellPicture>>,
102 #[serde(skip)]
104 pub spill: BTreeMap<CellRef, Value>,
105 #[serde(skip)]
107 pub spill_ranges: BTreeMap<CellRef, RangeRef>,
108 pub cols: BTreeMap<u32, LineInfo>,
109 pub rows: BTreeMap<u32, LineInfo>,
110 pub default_col_width: f32,
112 pub default_row_height: f32,
113 pub merges: Vec<RangeRef>,
114 pub freeze: Option<(u32, u32)>,
116 pub tab_color: Option<Color>,
117 pub visibility: Visibility,
118 pub show_gridlines: bool,
119 pub show_headings: bool,
120 pub show_formulas: bool,
121 pub show_zeros: bool,
122 #[serde(default = "yes")]
125 pub show_outline_symbols: bool,
126 #[serde(default = "yes")]
130 pub summary_below: bool,
131 #[serde(default = "yes")]
132 pub summary_right: bool,
133 pub right_to_left: bool,
134 pub zoom: u16,
135 pub cond_formats: Vec<CondFormat>,
136 pub validations: Vec<Validation>,
137 pub tables: Vec<Table>,
138 pub autofilter: Option<AutoFilter>,
139 #[serde(with = "crate::model::cellmap")]
140 pub comments: BTreeMap<CellRef, Comment>,
141 #[serde(with = "crate::model::cellmap")]
142 pub hyperlinks: BTreeMap<CellRef, Hyperlink>,
143 pub charts: Vec<Chart>,
144 pub images: Vec<Image>,
145 pub shapes: Vec<Shape>,
146 pub sparklines: Vec<Sparkline>,
147 #[serde(default)]
149 pub slicers: Vec<Slicer>,
150 #[serde(default, skip_serializing_if = "Vec::is_empty")]
153 pub controls: Vec<FormControl>,
154 #[serde(default)]
156 pub pivots: Vec<PivotTable>,
157 pub protection: Option<SheetProtection>,
158 #[serde(default, skip_serializing_if = "Vec::is_empty")]
160 pub edit_ranges: Vec<crate::model::EditRange>,
161 pub print: PrintSettings,
162 pub view_active: CellRef,
164 pub view_top_left: CellRef,
165 #[serde(default)]
167 pub view: SheetViewMode,
168 #[serde(skip)]
171 pub preserved: Option<Arc<crate::model::preserved::Preserved>>,
172}
173
174fn yes() -> bool {
175 true
176}
177
178pub const DEFAULT_COL_WIDTH: f32 = 64.0;
180pub const DEFAULT_ROW_HEIGHT: f32 = 20.0;
181
182pub fn points_to_chars(px: f64) -> f64 {
185 if px <= 12.0 { (px / 12.0 * 100.0).round() / 100.0 } else { ((px - 5.0) / 7.0 * 100.0 + 0.5).trunc() / 100.0 }
186}
187
188impl Sheet {
189 pub fn new(name: impl Into<String>) -> Sheet {
190 Sheet {
191 name: name.into(),
192 uid: SheetUid::fresh(),
193 file_id: 0,
194 cells: CellStore::new(),
195 cell_pictures: BTreeMap::new(),
196 spill: BTreeMap::new(),
197 spill_ranges: BTreeMap::new(),
198 cols: BTreeMap::new(),
199 rows: BTreeMap::new(),
200 default_col_width: DEFAULT_COL_WIDTH,
201 default_row_height: DEFAULT_ROW_HEIGHT,
202 merges: vec![],
203 freeze: None,
204 tab_color: None,
205 visibility: Visibility::Visible,
206 show_gridlines: true,
207 show_outline_symbols: true,
208 summary_below: true,
209 summary_right: true,
210 show_headings: true,
211 show_formulas: false,
212 show_zeros: true,
213 right_to_left: false,
214 zoom: 100,
215 cond_formats: vec![],
216 validations: vec![],
217 tables: vec![],
218 autofilter: None,
219 comments: BTreeMap::new(),
220 hyperlinks: BTreeMap::new(),
221 charts: vec![],
222 images: vec![],
223 shapes: vec![],
224 sparklines: vec![],
225 slicers: vec![],
226 controls: vec![],
227 pivots: vec![],
228 protection: None,
229 edit_ranges: vec![],
230 print: PrintSettings::default(),
231 view_active: CellRef::default(),
232 view_top_left: CellRef::default(),
233 view: SheetViewMode::Normal,
234 preserved: None,
235 }
236 }
237
238 pub fn cell(&self, c: CellRef) -> Option<&Cell> {
239 self.cells.get(c)
240 }
241 pub fn spill_anchor(&self, c: CellRef) -> Option<CellRef> {
244 self.spill_ranges.iter().find(|(anchor, range)| **anchor != c && range.contains(c)).map(|(anchor, _)| *anchor)
245 }
246
247 pub fn clear_spill(&mut self, r: RangeRef) {
251 if r.count() <= self.spill.len() as u64 {
252 for c in r.iter() {
253 self.spill.remove(&c);
254 }
255 } else {
256 self.spill.retain(|c, _| !r.contains(*c));
257 }
258 }
259
260 pub fn settle_spill(&mut self, anchor: CellRef) {
265 let Some(r) = self.spill_ranges.remove(&anchor) else { return };
266 let inside: Vec<CellRef> = self.spill.range(r.start..=r.end).map(|(c, _)| *c).filter(|c| r.contains(*c)).collect();
267 for c in inside {
268 let Some(v) = self.spill.remove(&c) else { continue };
269 if v.is_empty() || c == anchor {
270 continue;
271 }
272 match self.cells.get_mut(c) {
273 Some(cell) if cell.has_content() => {}
274 Some(cell) => cell.value = v,
275 None => {
276 let style = self.style_id(c);
277 self.cells.set(c, Cell::styled(v, style));
278 }
279 }
280 }
281 }
282
283 pub fn object_ref(&self, id: u32) -> Option<ObjRef> {
285 let kind = if self.charts.iter().any(|o| o.id == id) {
286 ObjKind::Chart
287 } else if self.images.iter().any(|o| o.id == id) {
288 ObjKind::Image
289 } else if let Some(s) = self.shapes.iter().find(|o| o.id == id) {
290 if s.kind == ShapeKind::Equation { ObjKind::Equation } else { ObjKind::Shape }
291 } else if self.slicers.iter().any(|o| o.id == id) {
292 ObjKind::Slicer
293 } else if self.controls.iter().any(|o| o.id == id) {
294 ObjKind::Control
295 } else {
296 return None;
297 };
298 Some(ObjRef::new(kind, id))
299 }
300
301 pub fn has_object(&self, o: ObjRef) -> bool {
304 self.object_anchor(o).is_some()
305 }
306
307 pub fn object_anchor(&self, o: ObjRef) -> Option<Anchor> {
309 match o.kind {
310 ObjKind::Chart => self.charts.iter().find(|c| c.id == o.id).map(|c| c.anchor),
311 ObjKind::Image => self.images.iter().find(|c| c.id == o.id).map(|c| c.anchor),
312 ObjKind::Shape => self.shapes.iter().find(|c| c.id == o.id).map(|c| c.anchor),
313 ObjKind::Slicer => self.slicers.iter().find(|c| c.id == o.id).map(|c| c.anchor),
314 ObjKind::Equation => self.shapes.iter().find(|c| c.id == o.id && c.kind == ShapeKind::Equation).map(|c| c.anchor),
315 ObjKind::Control => self.controls.iter().find(|c| c.id == o.id).map(|c| c.anchor),
316 }
317 }
318
319 pub fn object_anchor_mut(&mut self, o: ObjRef) -> Option<&mut Anchor> {
320 match o.kind {
321 ObjKind::Chart => self.charts.iter_mut().find(|c| c.id == o.id).map(|c| &mut c.anchor),
322 ObjKind::Image => self.images.iter_mut().find(|c| c.id == o.id).map(|c| &mut c.anchor),
323 ObjKind::Shape => self.shapes.iter_mut().find(|c| c.id == o.id).map(|c| &mut c.anchor),
324 ObjKind::Slicer => self.slicers.iter_mut().find(|c| c.id == o.id).map(|c| &mut c.anchor),
325 ObjKind::Equation => self.shapes.iter_mut().find(|c| c.id == o.id && c.kind == ShapeKind::Equation).map(|c| &mut c.anchor),
326 ObjKind::Control => self.controls.iter_mut().find(|c| c.id == o.id).map(|c| &mut c.anchor),
327 }
328 }
329
330 pub fn remove_object(&mut self, o: ObjRef) -> bool {
332 fn take<T>(v: &mut Vec<T>, f: impl Fn(&T) -> bool) -> bool {
333 let n = v.len();
334 v.retain(|x| !f(x));
335 v.len() != n
336 }
337 match o.kind {
338 ObjKind::Chart => take(&mut self.charts, |c| c.id == o.id),
339 ObjKind::Image => take(&mut self.images, |c| c.id == o.id),
340 ObjKind::Shape => take(&mut self.shapes, |c| c.id == o.id),
341 ObjKind::Slicer => take(&mut self.slicers, |c| c.id == o.id),
342 ObjKind::Equation => take(&mut self.shapes, |c| c.id == o.id && c.kind == ShapeKind::Equation),
343 ObjKind::Control => take(&mut self.controls, |c| c.id == o.id),
344 }
345 }
346
347 pub fn object_anchors(&self) -> impl Iterator<Item = &Anchor> {
351 self.charts
352 .iter()
353 .map(|o| &o.anchor)
354 .chain(self.images.iter().map(|o| &o.anchor))
355 .chain(self.shapes.iter().map(|o| &o.anchor))
356 .chain(self.slicers.iter().map(|o| &o.anchor))
357 .chain(self.controls.iter().map(|o| &o.anchor))
358 }
359
360 pub fn object_anchors_mut(&mut self) -> impl Iterator<Item = &mut Anchor> {
361 self.charts
362 .iter_mut()
363 .map(|o| &mut o.anchor)
364 .chain(self.images.iter_mut().map(|o| &mut o.anchor))
365 .chain(self.shapes.iter_mut().map(|o| &mut o.anchor))
366 .chain(self.slicers.iter_mut().map(|o| &mut o.anchor))
367 .chain(self.controls.iter_mut().map(|o| &mut o.anchor))
368 }
369 pub fn value(&self, c: CellRef) -> Value {
371 match self.cells.get(c) {
372 Some(cell) if !cell.value.is_empty() || cell.formula.is_some() => cell.value.clone(),
373 _ => self.spill.get(&c).cloned().unwrap_or(Value::Empty),
374 }
375 }
376 pub fn value_ref(&self, c: CellRef) -> Option<&Value> {
377 match self.cells.get(c) {
378 Some(cell) if !cell.value.is_empty() || cell.formula.is_some() => Some(&cell.value),
379 _ => self.spill.get(&c),
380 }
381 }
382 pub fn last_contiguous_nonempty(&self, col: u32, from_row: u32, cap: u32) -> u32 {
390 if from_row > cap {
391 return from_row.saturating_sub(1);
392 }
393 let mut row = from_row;
394 loop {
395 if row > cap {
396 return cap;
397 }
398 let c = CellRef::new(row, col);
399 match self.cells.contiguous_value_run(c, cap) {
400 Some(last) => row = last + 1,
401 None => {
402 let qualifies = self.value_ref(c).is_some_and(|v| !v.is_empty());
406 if qualifies {
407 row += 1;
408 } else {
409 return row.saturating_sub(1);
410 }
411 }
412 }
413 }
414 }
415 pub fn style_id(&self, c: CellRef) -> StyleId {
416 if let Some(cell) = self.cells.get(c) {
417 return cell.style;
418 }
419 if let Some(s) = self.rows.get(&c.row).and_then(|r| r.style) {
420 return s;
421 }
422 self.cols.get(&c.col).and_then(|r| r.style).unwrap_or_default()
423 }
424 pub fn set_value(&mut self, c: CellRef, v: Value) {
425 let style = self.style_id(c);
426 self.set_cell(c, Cell { value: v, formula: None, style, py: None });
427 }
428 pub fn set_cell(&mut self, c: CellRef, cell: Cell) {
430 self.cell_pictures.remove(&c);
431 self.cells.set(c, cell);
432 }
433 pub fn remove_cell(&mut self, c: CellRef) -> Option<Cell> {
434 self.cell_pictures.remove(&c);
435 self.cells.remove(c)
436 }
437 pub fn take_cells(&mut self, range: RangeRef) -> Vec<(CellRef, Cell)> {
439 self.cell_pictures.retain(|c, _| !range.contains(*c));
440 self.cells.take_range(range)
441 }
442 pub fn set_picture(&mut self, c: CellRef, picture: Arc<CellPicture>) {
444 let style = self.style_id(c);
445 self.cells.set(c, Cell { value: Value::Error(crate::base::CellError::Value), formula: None, style, py: None });
446 self.cell_pictures.insert(c, picture);
447 }
448 pub fn input_text(&self, c: CellRef) -> String {
449 if self.cell_pictures.contains_key(&c) { String::new() } else { self.cell(c).map(Cell::input_text).unwrap_or_default() }
450 }
451 pub fn set_style(&mut self, c: CellRef, style: StyleId) {
452 let mut cell = self.cells.get(c).cloned().unwrap_or_default();
453 cell.style = style;
454 self.cells.set(c, cell);
455 }
456
457 pub fn has_filter_button(&self, c: CellRef) -> bool {
461 self.autofilter.as_ref().is_some_and(|af| af.range.start.row == c.row && af.range.contains(c))
462 || self.tables.iter().any(|t| t.filter_button && t.header_row && t.range.start.row == c.row && t.range.contains(c))
463 }
464
465 pub fn filter_button_space(&self, c: CellRef) -> f32 {
470 if self.has_filter_button(c) { (12.5 * 96.0 / 72.0) / (self.zoom as f32 / 100.0).clamp(0.1, 1.0) } else { 0.0 }
471 }
472
473 pub fn col_width(&self, col: u32) -> f32 {
474 match self.cols.get(&col) {
475 Some(i) if i.hidden => 0.0,
476 Some(LineInfo { size: Some(w), .. }) => *w,
477 _ => self.default_col_width,
478 }
479 }
480 pub fn row_height(&self, row: u32) -> f32 {
481 match self.rows.get(&row) {
482 Some(i) if i.hidden => 0.0,
483 Some(LineInfo { size: Some(h), .. }) => *h,
484 _ => self.default_row_height,
485 }
486 }
487 pub fn is_row_hidden(&self, row: u32) -> bool {
488 self.rows.get(&row).is_some_and(|r| r.hidden)
489 }
490 pub fn is_row_filtered(&self, row: u32) -> bool {
494 if !self.is_row_hidden(row) {
495 return false;
496 }
497 let in_sheet_filter =
498 self.autofilter.as_ref().is_some_and(|af| !af.criteria.is_empty() && row > af.range.start.row && row <= af.range.end.row);
499 in_sheet_filter || self.tables.iter().any(|t| !t.filter.is_empty() && t.data_range().is_some_and(|r| row >= r.start.row && row <= r.end.row))
500 }
501 pub fn is_col_hidden(&self, col: u32) -> bool {
502 self.cols.get(&col).is_some_and(|r| r.hidden)
503 }
504 pub fn row_top(&self, row: u32) -> f64 {
506 let mut y = row as f64 * self.default_row_height as f64;
507 for (r, info) in self.rows.range(..row) {
508 let _ = r;
509 let h = if info.hidden { 0.0 } else { info.size.unwrap_or(self.default_row_height) };
510 y += h as f64 - self.default_row_height as f64;
511 }
512 y
513 }
514 pub fn col_left(&self, col: u32) -> f64 {
515 let mut x = col as f64 * self.default_col_width as f64;
516 for (_, info) in self.cols.range(..col) {
517 let w = if info.hidden { 0.0 } else { info.size.unwrap_or(self.default_col_width) };
518 x += w as f64 - self.default_col_width as f64;
519 }
520 x
521 }
522 pub fn row_at(&self, y: f64) -> u32 {
524 line_at(y, self.default_row_height as f64, &self.rows, crate::base::MAX_ROWS)
525 }
526 pub fn col_at(&self, x: f64) -> u32 {
527 line_at(x, self.default_col_width as f64, &self.cols, crate::base::MAX_COLS)
528 }
529
530 pub fn merge_at(&self, c: CellRef) -> Option<RangeRef> {
532 self.merges.iter().find(|m| m.contains(c)).copied()
533 }
534 pub fn table_at(&self, c: CellRef) -> Option<&Table> {
535 self.tables.iter().find(|t| t.range.contains(c))
536 }
537 pub fn used_range(&self) -> Option<RangeRef> {
538 let mut r = self.cells.used_range();
539 for sr in self.spill_ranges.values() {
540 r = Some(r.map_or(*sr, |x| x.union(sr)));
541 }
542 r
543 }
544 pub fn is_protected(&self) -> bool {
545 self.protection.is_some()
546 }
547}
548
549#[derive(Clone, Debug, Default)]
552pub struct LineIndex {
553 default: f64,
554 max: u32,
555 lines: Vec<(u32, f64, f64)>,
557}
558
559impl LineIndex {
560 pub fn new(custom: &BTreeMap<u32, LineInfo>, default: f32, max: u32) -> LineIndex {
561 let default = default as f64;
562 let mut lines = Vec::with_capacity(custom.len());
563 let mut pos = 0.0;
564 let mut prev = 0u32;
565 for (&line, info) in custom {
566 pos += (line - prev) as f64 * default;
567 let size = if info.hidden { 0.0 } else { info.size.map_or(default, |s| s as f64) };
568 lines.push((line, pos, size));
569 pos += size;
570 prev = line + 1;
571 }
572 LineIndex { default, max, lines }
573 }
574 pub fn start(&self, line: u32) -> f64 {
576 match self.lines.binary_search_by(|e| e.0.cmp(&line)) {
577 Ok(i) => self.lines.get(i).map_or(0.0, |e| e.1),
578 Err(0) => line as f64 * self.default,
579 Err(i) => match self.lines.get(i - 1) {
580 Some(&(l, p, s)) => p + s + (line - l - 1) as f64 * self.default,
581 None => line as f64 * self.default,
582 },
583 }
584 }
585 pub fn at(&self, pos: f64) -> u32 {
587 if pos <= 0.0 {
588 return 0;
589 }
590 let i = self.lines.partition_point(|e| e.1 <= pos);
592 let (base_line, base_pos) = match i.checked_sub(1).and_then(|k| self.lines.get(k)) {
593 Some(&(l, p, s)) => {
594 if pos < p + s {
595 return l.min(self.max - 1);
596 }
597 (l + 1, p + s)
598 }
599 None => (0, 0.0),
600 };
601 if self.default <= 0.0 {
602 return base_line.min(self.max - 1);
603 }
604 let k = ((pos - base_pos) / self.default).floor();
605 let r = base_line as f64 + k;
606 if r >= self.max as f64 { self.max - 1 } else { r as u32 }
607 }
608}
609
610fn line_at(pos: f64, default: f64, custom: &BTreeMap<u32, LineInfo>, max: u32) -> u32 {
612 if pos <= 0.0 {
613 return 0;
614 }
615 let mut start_pos = 0.0; let mut idx: u32 = 0;
617 for (&line, info) in custom.iter() {
618 let span = (line - idx) as f64 * default;
620 if pos < start_pos + span {
621 let k = ((pos - start_pos) / default).floor() as u32;
622 return (idx + k).min(max - 1);
623 }
624 start_pos += span;
625 let size = if info.hidden { 0.0 } else { info.size.map_or(default, |s| s as f64) };
626 if pos < start_pos + size {
627 return line.min(max - 1);
628 }
629 start_pos += size;
630 idx = line + 1;
631 }
632 if default <= 0.0 {
633 return idx.min(max - 1);
634 }
635 let k = ((pos - start_pos) / default).floor();
636 let r = idx as f64 + k;
637 if r >= max as f64 { max - 1 } else { r as u32 }
638}
639
640#[cfg(test)]
641mod tests {
642 use super::*;
643 use crate::model::cell::Formula;
644
645 #[test]
646 fn a_sheet_keeps_its_identity_through_clones_and_a_new_sheet_gets_its_own() {
647 let a = Sheet::new("A");
648 let b = Sheet::new("A");
649 assert_ne!(a.uid.get(), b.uid.get(), "two new sheets");
650 assert_ne!(a.uid.get(), 0);
651 let mut renamed = a.clone();
653 renamed.name = "B".into();
654 assert_eq!(renamed.uid.get(), a.uid.get());
655 assert_eq!(a, b);
657 let json = serde_json::to_string(&a).unwrap();
659 assert!(!json.contains("uid"), "{json}");
660 let read: Sheet = serde_json::from_str(&json).unwrap();
661 assert_ne!(read.uid.get(), a.uid.get());
662 assert_eq!(read, a);
663 }
664
665 #[test]
666 fn line_index_matches_linear() {
667 let mut s = Sheet::new("S");
668 for r in (0..5000).step_by(3) {
669 s.rows.insert(r, LineInfo { size: Some(10.0 + (r % 7) as f32), hidden: r % 11 == 0, ..Default::default() });
670 }
671 let idx = LineIndex::new(&s.rows, s.default_row_height, crate::base::MAX_ROWS);
672 for r in [0u32, 1, 2, 3, 4, 100, 999, 4998, 5000, 6000] {
673 assert!((idx.start(r) - s.row_top(r)).abs() < 1e-6, "start {r}");
674 }
675 for y in [0.0, 5.0, 33.3, 1000.0, 47_000.0, 90_000.0] {
676 assert_eq!(idx.at(y), s.row_at(y), "at {y}");
677 }
678 }
679
680 fn last_contiguous_nonempty_naive(s: &Sheet, col: u32, from_row: u32, cap: u32) -> u32 {
683 let mut row = from_row;
684 while row <= cap && s.value_ref(CellRef::new(row, col)).is_some_and(|v| !v.is_empty()) {
685 row += 1;
686 }
687 row.saturating_sub(1).max(from_row.saturating_sub(1))
688 }
689
690 struct Xorshift32(u32);
693 impl Xorshift32 {
694 fn next(&mut self) -> u32 {
695 self.0 ^= self.0 << 13;
696 self.0 ^= self.0 >> 17;
697 self.0 ^= self.0 << 5;
698 self.0
699 }
700 fn below(&mut self, n: u32) -> u32 {
701 self.next() % n.max(1)
702 }
703 }
704
705 #[test]
706 fn last_contiguous_nonempty_matches_naive() {
707 let mut rng = Xorshift32(0x9e3779b9);
708 for trial in 0..300u32 {
709 let mut s = Sheet::new("S");
710 let rows = rng.below(400);
711 for r in 0..rows {
714 match rng.below(5) {
715 0 => s.set_value(CellRef::new(r, 0), Value::Number(r as f64)),
716 1 => s.set_cell(CellRef::new(r, 0), Cell { formula: Some(Arc::new(Formula::new("1"))), ..Default::default() }),
717 2 => {
718 s.spill.insert(CellRef::new(r, 0), Value::Number(r as f64));
719 }
720 3 => {
721 s.set_value(CellRef::new(r, 0), Value::Text("".into()));
723 }
724 4 if rng.below(2) == 0 => {
725 s.spill.insert(CellRef::new(r, 0), Value::Number(r as f64));
728 s.set_cell(CellRef::new(r, 0), Cell { style: StyleId(1), ..Default::default() });
729 }
730 _ => {} }
732 }
733 for _ in 0..5 {
734 let from_row = rng.below(420);
735 let cap = from_row.saturating_add(rng.below(450));
736 let got = s.last_contiguous_nonempty(0, from_row, cap);
737 let want = last_contiguous_nonempty_naive(&s, 0, from_row, cap);
738 assert_eq!(got, want, "trial {trial}: col 0, from_row {from_row}, cap {cap}, rows up to {rows}");
739 }
740 }
741 }
742
743 #[test]
744 fn last_contiguous_nonempty_dense_run() {
745 let mut s = Sheet::new("S");
746 for r in 0..2000u32 {
747 s.set_value(CellRef::new(r, 2), Value::Number(r as f64));
748 }
749 assert_eq!(s.last_contiguous_nonempty(2, 0, 1_999), 1999);
750 assert_eq!(s.last_contiguous_nonempty(2, 0, 1_000), 1000); assert_eq!(s.last_contiguous_nonempty(2, 500, 1_999), 1999);
752 assert_eq!(s.last_contiguous_nonempty(2, 2000, 3000), 1999); assert_eq!(s.last_contiguous_nonempty(2, 2001, 3000), 2000); }
755
756 #[test]
757 fn last_contiguous_nonempty_formula_with_empty_value_ends_the_run() {
758 let mut s = Sheet::new("S");
759 s.set_value(CellRef::new(0, 1), Value::Number(1.0));
760 s.set_value(CellRef::new(1, 1), Value::Number(2.0));
761 s.set_cell(CellRef::new(2, 1), Cell { formula: Some(Arc::new(Formula::new("\"\""))), ..Default::default() });
765 s.set_value(CellRef::new(3, 1), Value::Number(4.0)); assert_eq!(s.last_contiguous_nonempty(1, 0, 100), 1);
767 assert_eq!(s.last_contiguous_nonempty(1, 2, 100), 1); }
769
770 #[test]
771 fn last_contiguous_nonempty_spill_extends_a_style_only_stored_row() {
772 let mut s = Sheet::new("S");
773 s.set_value(CellRef::new(0, 1), Value::Number(1.0));
774 s.spill.insert(CellRef::new(1, 1), Value::Number(2.0));
776 s.set_cell(CellRef::new(1, 1), Cell { style: StyleId(1), ..Default::default() });
777 s.set_value(CellRef::new(2, 1), Value::Number(3.0));
778 assert_eq!(s.last_contiguous_nonempty(1, 0, 100), 2);
779 assert_eq!(s.last_contiguous_nonempty(1, 1, 100), 2);
780 }
781
782 #[test]
783 fn last_contiguous_nonempty_spill_extends_an_unstored_row() {
784 let mut s = Sheet::new("S");
785 s.set_value(CellRef::new(0, 1), Value::Number(1.0));
786 s.spill.insert(CellRef::new(1, 1), Value::Number(2.0));
788 s.set_value(CellRef::new(2, 1), Value::Number(3.0));
789 assert_eq!(s.last_contiguous_nonempty(1, 0, 100), 2);
791 assert_eq!(s.last_contiguous_nonempty(1, 3, 100), 2); }
793
794 #[test]
795 fn geometry() {
796 let mut s = Sheet::new("S");
797 assert_eq!(s.row_top(3), 60.0);
798 s.rows.insert(1, LineInfo { size: Some(40.0), ..Default::default() });
799 s.rows.insert(2, LineInfo { hidden: true, ..Default::default() });
800 assert_eq!(s.row_top(1), 20.0);
801 assert_eq!(s.row_top(2), 60.0);
802 assert_eq!(s.row_top(3), 60.0);
803 assert_eq!(s.row_top(4), 80.0);
804 assert_eq!(s.row_at(10.0), 0);
805 assert_eq!(s.row_at(30.0), 1);
806 assert_eq!(s.row_at(59.0), 1);
807 assert_eq!(s.row_at(61.0), 3);
808 assert_eq!(s.row_at(85.0), 4);
809 assert_eq!(s.col_at(1e12), crate::base::MAX_COLS - 1);
810 assert_eq!(s.col_at(-5.0), 0);
811 assert_eq!(s.col_left(2), 128.0);
812 }
813
814 use crate::model::features::{ChartKind, EquationCache, LegendPos, ShapeFormat, ShapeKind};
815
816 fn push_object(s: &mut Sheet, kind: ObjKind, id: u32, anchor: Anchor) {
822 match kind {
823 ObjKind::Chart => s.charts.push(Chart {
824 id,
825 kind: ChartKind::default(),
826 anchor,
827 title: None,
828 series: vec![],
829 legend: LegendPos::default(),
830 data_labels: false,
831 data_table: None,
832 gridlines: false,
833 style: 0,
834 hole_size: 75,
835 first_slice_angle: 0,
836 x_title: None,
837 y_title: None,
838 source: None,
839 by_rows: false,
840 pivot: None,
841 }),
842 ObjKind::Image => s.images.push(Image { id, anchor, data: Arc::new(vec![]), mime: String::new(), alt: String::new() }),
843 ObjKind::Shape | ObjKind::Equation => s.shapes.push(Shape {
844 id,
845 kind: if kind == ObjKind::Equation { ShapeKind::Equation } else { ShapeKind::default() },
846 anchor,
847 fill: Color::default(),
848 line: Color::default(),
849 text: String::new(),
850 format: ShapeFormat::default(),
851 math: EquationCache::default(),
852 }),
853 ObjKind::Slicer => {
854 s.slicers.push(Slicer { id, table: String::new(), column: String::new(), caption: String::new(), anchor, selected: None, columns: 1 })
855 }
856 ObjKind::Control => s.controls.push(FormControl { id, anchor, ..Default::default() }),
857 }
858 }
859
860 #[test]
865 fn object_anchor_accessors_visit_the_same_objects_in_the_same_order() {
866 let mut s = Sheet::new("S");
867 let inserted = [
870 (ObjKind::Slicer, 7),
871 (ObjKind::Chart, 3),
872 (ObjKind::Image, 11),
873 (ObjKind::Shape, 2),
874 (ObjKind::Chart, 9),
875 (ObjKind::Slicer, 1),
876 (ObjKind::Image, 4),
877 (ObjKind::Equation, 6),
878 (ObjKind::Control, 13),
879 (ObjKind::Shape, 12),
880 (ObjKind::Equation, 8),
881 (ObjKind::Control, 15),
882 ];
883 for &(kind, id) in &inserted {
884 push_object(&mut s, kind, id, Anchor { cell: CellRef::new(id, 0), ..Default::default() });
885 }
886 let per_kind = s.charts.len() + s.images.len() + s.shapes.len() + s.slicers.len() + s.controls.len();
887 assert_eq!(per_kind, inserted.len());
888 assert_eq!(s.object_anchors().count(), per_kind, "object_anchors visits every collection");
889 assert_eq!(s.object_anchors_mut().count(), per_kind, "object_anchors_mut visits every collection");
890 for (i, a) in s.object_anchors_mut().enumerate() {
893 a.dx = i as f32;
894 }
895 let marks: Vec<f32> = s.object_anchors().map(|a| a.dx).collect();
896 assert_eq!(marks, (0..per_kind).map(|i| i as f32).collect::<Vec<f32>>());
897 let collection = |k: ObjKind| if k == ObjKind::Equation { ObjKind::Shape } else { k };
900 let documented: Vec<u32> = [ObjKind::Chart, ObjKind::Image, ObjKind::Shape, ObjKind::Slicer, ObjKind::Control]
901 .iter()
902 .flat_map(|&k| inserted.iter().filter(move |(kind, _)| collection(*kind) == k).map(|&(_, id)| id))
903 .collect();
904 let visited: Vec<u32> = s.object_anchors().map(|a| a.cell.row).collect();
905 assert_eq!(visited, documented);
906 for &(kind, id) in &inserted {
909 let position = documented.iter().position(|&x| x == id).map(|p| p as f32);
910 assert_eq!(s.object_anchor(ObjRef::new(kind, id)).map(|a| a.dx), position, "{kind:?} {id}");
911 }
912 }
913}
914
915mod picture_map {
917 use crate::base::CellRef;
918 use crate::model::cell::CellPicture;
919 use serde::{Deserialize, Deserializer, Serialize, Serializer};
920 use std::collections::BTreeMap;
921 use std::sync::Arc;
922 pub fn serialize<S: Serializer>(map: &BTreeMap<CellRef, Arc<CellPicture>>, serializer: S) -> Result<S::Ok, S::Error> {
923 map.iter().collect::<Vec<_>>().serialize(serializer)
924 }
925 pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<BTreeMap<CellRef, Arc<CellPicture>>, D::Error> {
926 Ok(Vec::<(CellRef, Arc<CellPicture>)>::deserialize(deserializer)?.into_iter().collect())
927 }
928}