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