1use crate::constants::{self, LAST_COLUMN, LAST_ROW};
2use crate::expressions::types::CellReferenceIndex;
3use crate::expressions::utils::{is_valid_column_number, is_valid_row};
4use crate::model::CellStructure;
5use crate::{expressions::token::Error, types::*};
6
7use std::collections::HashMap;
8
9#[derive(Debug, PartialEq, Eq)]
10pub struct WorksheetDimension {
11 pub min_row: i32,
12 pub max_row: i32,
13 pub min_column: i32,
14 pub max_column: i32,
15}
16
17#[derive(Clone, Copy, PartialEq, Eq)]
18pub enum NavigationDirection {
19 Left,
20 Right,
21 Up,
22 Down,
23}
24
25impl Worksheet {
26 pub fn get_name(&self) -> String {
27 self.name.clone()
28 }
29
30 pub fn get_sheet_id(&self) -> u32 {
31 self.sheet_id
32 }
33
34 pub fn set_name(&mut self, name: &str) {
35 self.name = name.to_string();
36 }
37
38 pub fn cell(&self, row: i32, column: i32) -> Option<&Cell> {
39 self.sheet_data.get(&row)?.get(&column)
40 }
41
42 pub(crate) fn cell_mut(&mut self, row: i32, column: i32) -> Option<&mut Cell> {
43 self.sheet_data.get_mut(&row)?.get_mut(&column)
44 }
45
46 pub(crate) fn update_cell(
47 &mut self,
48 row: i32,
49 column: i32,
50 new_cell: Cell,
51 ) -> Result<(), String> {
52 if !is_valid_row(row) || !is_valid_column_number(column) {
54 return Err("Incorrect row or column".to_string());
55 }
56
57 match self.sheet_data.get_mut(&row) {
58 Some(column_data) => match column_data.get(&column) {
59 Some(_cell) => {
60 column_data.insert(column, new_cell);
61 }
62 None => {
63 column_data.insert(column, new_cell);
64 }
65 },
66 None => {
67 let mut column_data = HashMap::new();
68 column_data.insert(column, new_cell);
69 self.sheet_data.insert(row, column_data);
70 }
71 }
72 Ok(())
73 }
74
75 fn get_row_column_style(&self, row_index: i32, column_index: i32) -> i32 {
77 let rows = &self.rows;
78 for row in rows {
79 if row.r == row_index {
80 if row.custom_format {
81 return row.s;
82 }
83 break;
84 }
85 }
86 let cols = &self.cols;
87 for column in cols.iter() {
88 let min = column.min;
89 let max = column.max;
90 if column_index >= min && column_index <= max {
91 return column.style.unwrap_or(0);
92 }
93 }
94 0
95 }
96
97 pub fn get_style(&self, row: i32, column: i32) -> i32 {
98 match self.sheet_data.get(&row) {
99 Some(column_data) => match column_data.get(&column) {
100 Some(cell) => cell.get_style(),
101 None => self.get_row_column_style(row, column),
102 },
103 None => self.get_row_column_style(row, column),
104 }
105 }
106
107 pub fn set_style(&mut self, style_index: i32) -> Result<(), String> {
108 self.cols = vec![Col {
109 min: 1,
110 max: constants::LAST_COLUMN,
111 width: constants::DEFAULT_COLUMN_WIDTH,
112 custom_width: false,
113 style: Some(style_index),
114 hidden: false,
115 }];
116 Ok(())
117 }
118
119 pub fn set_column_style(&mut self, column: i32, style_index: i32) -> Result<(), String> {
120 let width = self
121 .get_column_width(column)
122 .unwrap_or(constants::DEFAULT_COLUMN_WIDTH);
123 let hidden = self.is_column_hidden(column)?;
124 self.set_column_width_and_style(column, width, hidden, Some(style_index))
125 }
126
127 pub fn set_row_style(&mut self, row: i32, style_index: i32) -> Result<(), String> {
128 let custom_format = style_index != 0;
130 for r in self.rows.iter_mut() {
131 if r.r == row {
132 r.s = style_index;
133 r.custom_format = custom_format;
134 return Ok(());
135 }
136 }
137 self.rows.push(Row {
138 height: constants::DEFAULT_ROW_HEIGHT / constants::ROW_HEIGHT_FACTOR,
139 r: row,
140 custom_format,
141 custom_height: false,
142 s: style_index,
143 hidden: false,
144 });
145 Ok(())
146 }
147
148 pub fn delete_row_style(&mut self, row: i32) -> Result<(), String> {
149 let mut index = None;
150 for (i, r) in self.rows.iter().enumerate() {
151 if r.r == row {
152 index = Some(i);
153 break;
154 }
155 }
156 if let Some(i) = index {
157 if let Some(r) = self.rows.get_mut(i) {
158 r.s = 0;
159 r.custom_format = false;
160 }
161 }
162 Ok(())
163 }
164
165 pub fn delete_column_style(&mut self, column: i32) -> Result<(), String> {
166 if !is_valid_column_number(column) {
167 return Err(format!("Column number '{column}' is not valid."));
168 }
169 let cols = &mut self.cols;
170
171 let mut index = 0;
172 let mut split = false;
173 for c in cols.iter_mut() {
174 let min = c.min;
175 let max = c.max;
176 if min <= column && column <= max {
177 split = true;
179 break;
180 }
181 if column < min {
182 break;
184 }
185 index += 1;
186 }
187 if split {
188 let min = cols[index].min;
189 let max = cols[index].max;
190 let custom_width = cols[index].custom_width;
191 let width = cols[index].width;
192 let pre = Col {
193 min,
194 max: column - 1,
195 width,
196 custom_width,
197 style: cols[index].style,
198 hidden: cols[index].hidden,
199 };
200 let col = Col {
201 min: column,
202 max: column,
203 width,
204 custom_width,
205 style: None,
206 hidden: false,
207 };
208 let post = Col {
209 min: column + 1,
210 max,
211 width,
212 custom_width,
213 style: cols[index].style,
214 hidden: cols[index].hidden,
215 };
216 cols.remove(index);
217 if column != max {
218 cols.insert(index, post);
219 }
220 if custom_width {
221 cols.insert(index, col);
222 }
223 if column != min {
224 cols.insert(index, pre);
225 }
226 }
227 Ok(())
228 }
229
230 pub fn set_cell_style(
231 &mut self,
232 row: i32,
233 column: i32,
234 style_index: i32,
235 ) -> Result<(), String> {
236 match self.cell_mut(row, column) {
237 Some(cell) => {
238 cell.set_style(style_index);
239 }
240 None => {
241 self.cell_clear_contents_with_style(row, column, style_index)?;
242 }
243 }
244 Ok(())
245
246 }
248
249 pub fn set_cell_with_formula(
250 &mut self,
251 row: i32,
252 column: i32,
253 index: i32,
254 style: i32,
255 ) -> Result<(), String> {
256 let cell = Cell::new_formula(index, style);
257 self.update_cell(row, column, cell)
258 }
259
260 pub fn set_cell_with_dynamic_formula(
261 &mut self,
262 row: i32,
263 column: i32,
264 index: i32,
265 style: i32,
266 width: i32,
267 height: i32,
268 ) -> Result<(), String> {
269 let cell = Cell::ArrayFormula {
270 f: index,
271 s: style,
272 r: (width, height),
273 kind: ArrayKind::Dynamic,
274 v: FormulaValue::Unevaluated,
275 };
276 self.update_cell(row, column, cell)
277 }
278
279 pub fn set_cell_with_array_formula(
280 &mut self,
281 row: i32,
282 column: i32,
283 index: i32,
284 style: i32,
285 width: i32,
286 height: i32,
287 ) -> Result<(), String> {
288 let cell = Cell::ArrayFormula {
289 f: index,
290 s: style,
291 r: (width, height),
292 kind: ArrayKind::Cse,
293 v: FormulaValue::Unevaluated,
294 };
295 self.update_cell(row, column, cell)
296 }
297
298 pub fn set_cell_with_number(
299 &mut self,
300 row: i32,
301 column: i32,
302 value: f64,
303 style: i32,
304 ) -> Result<(), String> {
305 let cell = Cell::new_number(value, style);
306 self.update_cell(row, column, cell)
307 }
308
309 pub fn set_cell_with_string(
310 &mut self,
311 row: i32,
312 column: i32,
313 index: i32,
314 style: i32,
315 ) -> Result<(), String> {
316 let cell = Cell::new_string(index, style);
317 self.update_cell(row, column, cell)
318 }
319
320 pub fn set_cell_with_boolean(
321 &mut self,
322 row: i32,
323 column: i32,
324 value: bool,
325 style: i32,
326 ) -> Result<(), String> {
327 let cell = Cell::new_boolean(value, style);
328 self.update_cell(row, column, cell)
329 }
330
331 pub fn set_cell_with_error(
332 &mut self,
333 row: i32,
334 column: i32,
335 error: Error,
336 style: i32,
337 ) -> Result<(), String> {
338 let cell = Cell::new_error(error, style);
339 self.update_cell(row, column, cell)
340 }
341
342 pub fn cell_clear_contents(&mut self, row: i32, column: i32) -> Result<(), String> {
343 let s = self.get_style(row, column);
344 let cell = Cell::EmptyCell { s };
345 self.update_cell(row, column, cell)
346 }
347
348 pub fn cell_clear_contents_with_style(
349 &mut self,
350 row: i32,
351 column: i32,
352 style: i32,
353 ) -> Result<(), String> {
354 let cell = Cell::EmptyCell { s: style };
355 self.update_cell(row, column, cell)
356 }
357
358 pub fn set_frozen_rows(&mut self, frozen_rows: i32) -> Result<(), String> {
359 if frozen_rows < 0 {
360 return Err("Frozen rows cannot be negative".to_string());
361 }
362 if frozen_rows >= constants::LAST_ROW {
363 return Err("Too many rows".to_string());
364 }
365 self.frozen_rows = frozen_rows;
366 Ok(())
367 }
368
369 pub fn set_frozen_columns(&mut self, frozen_columns: i32) -> Result<(), String> {
370 if frozen_columns < 0 {
371 return Err("Frozen columns cannot be negative".to_string());
372 }
373 if frozen_columns >= constants::LAST_COLUMN {
374 return Err("Too many columns".to_string());
375 }
376 self.frozen_columns = frozen_columns;
377 Ok(())
378 }
379
380 pub fn set_row_hidden(&mut self, row: i32, hidden: bool) -> Result<(), String> {
382 if !is_valid_row(row) {
383 return Err(format!("Row number '{row}' is not valid."));
384 }
385
386 let rows = &mut self.rows;
387 for r in rows.iter_mut() {
388 if r.r == row {
389 r.hidden = hidden;
390 return Ok(());
391 }
392 }
393 rows.push(Row {
394 height: constants::DEFAULT_ROW_HEIGHT / constants::ROW_HEIGHT_FACTOR,
395 r: row,
396 custom_format: false,
397 custom_height: false,
398 s: 0,
399 hidden,
400 });
401 Ok(())
402 }
403
404 pub fn set_row_height(&mut self, row: i32, height: f64) -> Result<(), String> {
410 if !is_valid_row(row) {
411 return Err(format!("Row number '{row}' is not valid."));
412 }
413 if height < 0.0 {
414 return Err(format!("Can not set a negative height: {height}"));
415 }
416 let hidden = self.is_row_hidden(row)?;
417 let rows = &mut self.rows;
418 for r in rows.iter_mut() {
419 if r.r == row {
420 r.height = height / constants::ROW_HEIGHT_FACTOR;
421 r.custom_height = true;
422 return Ok(());
423 }
424 }
425 rows.push(Row {
426 height: height / constants::ROW_HEIGHT_FACTOR,
427 r: row,
428 custom_format: false,
429 custom_height: true,
430 s: 0,
431 hidden,
432 });
433 Ok(())
434 }
435
436 pub fn set_column_width(&mut self, column: i32, width: f64) -> Result<(), String> {
442 let style = self.get_column_style(column)?;
443 let hidden = self.is_column_hidden(column)?;
444 self.set_column_width_and_style(column, width, hidden, style)
445 }
446
447 pub fn set_column_hidden(&mut self, column: i32, hidden: bool) -> Result<(), String> {
448 let width = self
449 .get_actual_column_width(column)
450 .unwrap_or(constants::DEFAULT_COLUMN_WIDTH);
451 let style = self.get_column_style(column)?;
452 self.set_column_width_and_style(column, width, hidden, style)
453 }
454
455 pub(crate) fn set_column_width_and_style(
456 &mut self,
457 column: i32,
458 width: f64,
459 hidden: bool,
460 style: Option<i32>,
461 ) -> Result<(), String> {
462 if !is_valid_column_number(column) {
463 return Err(format!("Column number '{column}' is not valid."));
464 }
465 if width < 0.0 {
466 return Err(format!("Can not set a negative width: {width}"));
467 }
468 let cols = &mut self.cols;
469 let mut col = Col {
470 min: column,
471 max: column,
472 width: width / constants::COLUMN_WIDTH_FACTOR,
473 custom_width: width != constants::DEFAULT_COLUMN_WIDTH,
474 style,
475 hidden,
476 };
477 let mut index = 0;
478 let mut split = false;
479 for c in cols.iter_mut() {
480 let min = c.min;
481 let max = c.max;
482 if min <= column && column <= max {
483 if min == column && max == column {
484 c.style = style;
485 c.width = width / constants::COLUMN_WIDTH_FACTOR;
486 c.custom_width = width != constants::DEFAULT_COLUMN_WIDTH;
487 c.hidden = hidden;
488 return Ok(());
489 }
490 split = true;
491 break;
492 }
493 if column < min {
494 break;
496 }
497 index += 1;
498 }
499 if split {
500 let min = cols[index].min;
501 let max = cols[index].max;
502 let pre = Col {
503 min,
504 max: column - 1,
505 width: cols[index].width,
506 custom_width: cols[index].custom_width,
507 style: cols[index].style,
508 hidden: cols[index].hidden,
509 };
510 let post = Col {
511 min: column + 1,
512 max,
513 width: cols[index].width,
514 custom_width: cols[index].custom_width,
515 style: cols[index].style,
516 hidden: cols[index].hidden,
517 };
518 col.style = cols[index].style;
519 cols.remove(index);
520 if column != max {
521 cols.insert(index, post);
522 }
523 cols.insert(index, col);
524 if column != min {
525 cols.insert(index, pre);
526 }
527 } else {
528 cols.insert(index, col);
529 }
530 Ok(())
531 }
532
533 pub fn get_column_width(&self, column: i32) -> Result<f64, String> {
535 if !is_valid_column_number(column) {
536 return Err(format!("Column number '{column}' is not valid."));
537 }
538
539 let cols = &self.cols;
540 for col in cols {
541 let min = col.min;
542 let max = col.max;
543 if column >= min && column <= max {
544 if col.hidden {
545 return Ok(0.0);
546 }
547 if col.custom_width {
548 return Ok(col.width * constants::COLUMN_WIDTH_FACTOR);
549 }
550 break;
551 }
552 }
553 Ok(constants::DEFAULT_COLUMN_WIDTH)
554 }
555
556 pub fn get_actual_column_width(&self, column: i32) -> Result<f64, String> {
558 if !is_valid_column_number(column) {
559 return Err(format!("Column number '{column}' is not valid."));
560 }
561
562 let cols = &self.cols;
563 for col in cols {
564 let min = col.min;
565 let max = col.max;
566 if column >= min && column <= max {
567 if col.custom_width {
568 return Ok(col.width * constants::COLUMN_WIDTH_FACTOR);
569 }
570 break;
571 }
572 }
573 Ok(constants::DEFAULT_COLUMN_WIDTH)
574 }
575
576 pub fn is_column_hidden(&self, column: i32) -> Result<bool, String> {
578 if !is_valid_column_number(column) {
579 return Err(format!("Column number '{column}' is not valid."));
580 }
581
582 let cols = &self.cols;
583 for col in cols {
584 let min = col.min;
585 let max = col.max;
586 if column >= min && column <= max {
587 return Ok(col.hidden);
588 }
589 }
590 Ok(false)
591 }
592
593 pub fn is_row_hidden(&self, row: i32) -> Result<bool, String> {
595 if !is_valid_row(row) {
596 return Err(format!("Row number '{row}' is not valid."));
597 }
598 let rows = &self.rows;
599 for r in rows {
600 if r.r == row {
601 return Ok(r.hidden);
602 }
603 }
604 Ok(false)
605 }
606
607 pub fn get_column_style(&self, column: i32) -> Result<Option<i32>, String> {
609 if !is_valid_column_number(column) {
610 return Err(format!("Column number '{column}' is not valid."));
611 }
612
613 let cols = &self.cols;
614 for col in cols {
615 let min = col.min;
616 let max = col.max;
617 if column >= min && column <= max {
618 return Ok(col.style);
619 }
620 }
621 Ok(None)
622 }
623
624 pub(crate) fn column_cell_references(
626 &self,
627 column: i32,
628 ) -> Result<Vec<CellReferenceIndex>, String> {
629 let mut column_cell_references: Vec<CellReferenceIndex> = Vec::new();
630 if !is_valid_column_number(column) {
631 return Err(format!("Column number '{column}' is not valid."));
632 }
633
634 for row in self.sheet_data.keys() {
635 if self.cell(*row, column).is_some() {
636 column_cell_references.push(CellReferenceIndex {
637 sheet: self.sheet_id,
638 row: *row,
639 column,
640 });
641 }
642 }
643 Ok(column_cell_references)
644 }
645
646 pub(crate) fn remove_cell(&mut self, row: i32, column: i32) -> Result<(), String> {
647 if let Some(row_data) = self.sheet_data.get_mut(&row) {
648 row_data.remove(&column);
649 if row_data.is_empty() {
650 self.sheet_data.remove(&row);
651 }
652 }
653 Ok(())
654 }
655
656 pub fn row_height(&self, row: i32) -> Result<f64, String> {
658 if !is_valid_row(row) {
659 return Err(format!("Row number '{row}' is not valid."));
660 }
661
662 let rows = &self.rows;
663 for r in rows {
664 if r.r == row {
665 if r.hidden {
666 return Ok(0.0);
667 }
668 return Ok(r.height * constants::ROW_HEIGHT_FACTOR);
669 }
670 }
671 Ok(constants::DEFAULT_ROW_HEIGHT)
672 }
673
674 pub fn dimension(&self) -> WorksheetDimension {
676 if self.sheet_data.is_empty() {
678 return WorksheetDimension {
679 min_row: 1,
680 max_row: 1,
681 min_column: 1,
682 max_column: 1,
683 };
684 }
685
686 let mut row_range: Option<(i32, i32)> = None;
687 let mut column_range: Option<(i32, i32)> = None;
688
689 for (row_index, columns) in &self.sheet_data {
690 row_range = if let Some((current_min, current_max)) = row_range {
691 Some((current_min.min(*row_index), current_max.max(*row_index)))
692 } else {
693 Some((*row_index, *row_index))
694 };
695
696 for column_index in columns.keys() {
697 column_range = if let Some((current_min, current_max)) = column_range {
698 Some((
699 current_min.min(*column_index),
700 current_max.max(*column_index),
701 ))
702 } else {
703 Some((*column_index, *column_index))
704 }
705 }
706 }
707
708 let dimension = if let Some((min_row, max_row)) = row_range {
709 if let Some((min_column, max_column)) = column_range {
710 Some(WorksheetDimension {
711 min_row,
712 min_column,
713 max_row,
714 max_column,
715 })
716 } else {
717 None
718 }
719 } else {
720 None
721 };
722
723 dimension.unwrap_or(WorksheetDimension {
724 min_row: 1,
725 max_row: 1,
726 min_column: 1,
727 max_column: 1,
728 })
729 }
730
731 pub fn is_empty_cell(&self, row: i32, column: i32) -> Result<bool, String> {
734 if !is_valid_column_number(column) || !is_valid_row(row) {
735 return Err("Row or column is outside valid range.".to_string());
736 }
737
738 let is_empty = if let Some(data_row) = self.sheet_data.get(&row) {
739 if let Some(cell) = data_row.get(&column) {
740 matches!(cell, Cell::EmptyCell { .. })
741 } else {
742 true
743 }
744 } else {
745 true
746 };
747
748 Ok(is_empty)
749 }
750
751 pub(crate) fn get_cell_spill(&self, row: i32, column: i32) -> Result<(i32, i32), String> {
759 if !is_valid_column_number(column) || !is_valid_row(row) {
760 return Err("Row or column is outside valid range.".to_string());
761 }
762
763 let cell = match self.cell(row, column) {
764 Some(c) => c,
765 None => return Err("Cell does not exist.".to_string()),
766 };
767 match cell {
768 Cell::ArrayFormula { r, .. } => Ok((r.0, r.1)),
769 Cell::CellFormula { .. } => Ok((1, 1)),
770 _ => Err("Cell does not contain a formula.".to_string()),
771 }
772 }
773
774 pub(crate) fn get_cell_structure(
776 &self,
777 row: i32,
778 column: i32,
779 ) -> Result<CellStructure, String> {
780 if !is_valid_column_number(column) || !is_valid_row(row) {
781 return Err("Row or column is outside valid range.".to_string());
782 }
783
784 let cell = match self.cell(row, column) {
785 Some(c) => c,
786 None => return Ok(CellStructure::SingleCell),
787 };
788 match cell {
789 Cell::ArrayFormula {
790 r,
791 kind: ArrayKind::Cse,
792 ..
793 } => Ok(CellStructure::ArrayFormula { range: *r }),
794 Cell::ArrayFormula {
795 r,
796 kind: ArrayKind::Dynamic,
797 ..
798 } => Ok(CellStructure::DynamicFormula { range: *r }),
799 Cell::SpillCell { a, .. } => {
800 let anchor_cell = match self.cell(a.0, a.1) {
801 Some(c) => c,
802 None => return Err("Invalid spill reference".to_string()),
803 };
804 match anchor_cell {
805 Cell::ArrayFormula {
806 r,
807 kind: ArrayKind::Cse,
808 ..
809 } => Ok(CellStructure::SpillArray {
810 anchor: (a.0, a.1),
811 range: *r,
812 }),
813 Cell::ArrayFormula {
814 r,
815 kind: ArrayKind::Dynamic,
816 ..
817 } => Ok(CellStructure::SpillDynamic {
818 anchor: (a.0, a.1),
819 range: *r,
820 }),
821 _ => Err("Spill cell does not reference an array formula".to_string()),
822 }
823 }
824 _ => Ok(CellStructure::SingleCell),
825 }
826 }
827
828 pub fn navigate_to_edge_in_direction(
837 &self,
838 row: i32,
839 column: i32,
840 direction: NavigationDirection,
841 ) -> Result<(i32, i32), String> {
842 if !is_valid_column_number(column) || !is_valid_row(row) {
843 return Err("Row or column is outside valid range.".to_string());
844 }
845
846 let start_cell = (row, column);
847 let neighbour_cell = if let Some(cell) = step_in_direction(start_cell, direction) {
848 cell
849 } else {
850 return Ok((start_cell.0, start_cell.1));
851 };
852
853 if self.is_empty_cell(start_cell.0, start_cell.1)? {
854 let found_cells = walk_in_direction(start_cell, direction, |(row, column)| {
856 Ok(!self.is_empty_cell(row, column)?)
857 })?;
858 Ok(match found_cells.found_cell {
859 Some(cell) => cell,
860 None => found_cells.previous_cell,
861 })
862 } else {
863 if self.is_empty_cell(neighbour_cell.0, neighbour_cell.1)? {
866 let found_cells = walk_in_direction(start_cell, direction, |(row, column)| {
867 Ok(!self.is_empty_cell(row, column)?)
868 })?;
869 Ok(match found_cells.found_cell {
870 Some(cell) => cell,
871 None => found_cells.previous_cell,
872 })
873 } else {
874 let found_cells = walk_in_direction(start_cell, direction, |(row, column)| {
875 self.is_empty_cell(row, column)
876 })?;
877 Ok(found_cells.previous_cell)
878 }
879 }
880 }
881}
882
883struct WalkFoundCells {
884 found_cell: Option<(i32, i32)>,
886 previous_cell: (i32, i32),
889}
890
891fn walk_in_direction<F>(
894 start_cell: (i32, i32),
895 direction: NavigationDirection,
896 predicate: F,
897) -> Result<WalkFoundCells, String>
898where
899 F: Fn((i32, i32)) -> Result<bool, String>,
900{
901 let mut previous_cell = start_cell;
902 let mut current_cell = step_in_direction(start_cell, direction);
903 while let Some(cell) = current_cell {
904 if !predicate((cell.0, cell.1))? {
905 previous_cell = cell;
906 current_cell = step_in_direction(cell, direction);
907 } else {
908 break;
909 }
910 }
911 Ok(WalkFoundCells {
912 found_cell: current_cell,
913 previous_cell,
914 })
915}
916
917fn step_in_direction(
920 (row, column): (i32, i32),
921 direction: NavigationDirection,
922) -> Option<(i32, i32)> {
923 if (row == 1 && direction == NavigationDirection::Up)
924 || (row == LAST_ROW && direction == NavigationDirection::Down)
925 || (column == 1 && direction == NavigationDirection::Left)
926 || (column == LAST_COLUMN && direction == NavigationDirection::Right)
927 {
928 return None;
929 }
930
931 Some(match direction {
932 NavigationDirection::Left => (row, column - 1),
933 NavigationDirection::Right => (row, column + 1),
934 NavigationDirection::Up => (row - 1, column),
935 NavigationDirection::Down => (row + 1, column),
936 })
937}