excel_cli/utils/
cell_navigation.rs

1use crate::excel::Sheet;
2
3/// Navigation direction
4#[derive(Debug, Clone, Copy)]
5pub enum Direction {
6    Left,
7    Right,
8    Up,
9    Down,
10}
11
12/// Find non-empty cell in specified direction
13///
14/// Returns the position of found cell, or None if already at boundary
15#[must_use]
16pub fn find_non_empty_cell(
17    sheet: &Sheet,
18    current_pos: (usize, usize),
19    direction: Direction,
20    max_bounds: (usize, usize),
21) -> Option<(usize, usize)> {
22    let (row, col) = current_pos;
23    let (max_row, max_col) = max_bounds;
24
25    // Check if already at boundary
26    match direction {
27        Direction::Left if col <= 1 => return None,
28        Direction::Right if col >= max_col => return None,
29        Direction::Up if row <= 1 => return None,
30        Direction::Down if row >= max_row => return None,
31        _ => {}
32    }
33
34    // Check if current cell is empty
35    let current_cell_is_empty = row >= sheet.data.len()
36        || col >= sheet.data[row].len()
37        || sheet.data[row][col].value.is_empty();
38
39    if current_cell_is_empty {
40        // Current cell is empty, find first non-empty cell
41        match direction {
42            Direction::Left => {
43                for c in (1..col).rev() {
44                    if row < sheet.data.len()
45                        && c < sheet.data[row].len()
46                        && !sheet.data[row][c].value.is_empty()
47                    {
48                        return Some((row, c));
49                    }
50                }
51                // Return boundary if no non-empty cell found
52                Some((row, 1))
53            }
54            Direction::Right => {
55                for c in (col + 1)..=max_col {
56                    if row < sheet.data.len()
57                        && c < sheet.data[row].len()
58                        && !sheet.data[row][c].value.is_empty()
59                    {
60                        return Some((row, c));
61                    }
62                }
63                // Return boundary if no non-empty cell found
64                Some((row, max_col))
65            }
66            Direction::Up => {
67                for r in (1..row).rev() {
68                    if r < sheet.data.len()
69                        && col < sheet.data[r].len()
70                        && !sheet.data[r][col].value.is_empty()
71                    {
72                        return Some((r, col));
73                    }
74                }
75                // Return boundary if no non-empty cell found
76                Some((1, col))
77            }
78            Direction::Down => {
79                for r in (row + 1)..=max_row {
80                    if r < sheet.data.len()
81                        && col < sheet.data[r].len()
82                        && !sheet.data[r][col].value.is_empty()
83                    {
84                        return Some((r, col));
85                    }
86                }
87                // Return boundary if no non-empty cell found
88                Some((max_row, col))
89            }
90        }
91    } else {
92        // Current cell is non-empty, find boundary
93        match direction {
94            Direction::Left => {
95                let mut last_non_empty = col;
96
97                for c in (1..col).rev() {
98                    if row < sheet.data.len() && c < sheet.data[row].len() {
99                        if sheet.data[row][c].value.is_empty() {
100                            return Some((row, c + 1));
101                        }
102                        last_non_empty = c;
103                    } else {
104                        return Some((row, c + 1));
105                    }
106                }
107
108                Some((row, last_non_empty))
109            }
110            Direction::Right => {
111                let mut last_non_empty = col;
112
113                for c in (col + 1)..=max_col {
114                    if row < sheet.data.len() && c < sheet.data[row].len() {
115                        if sheet.data[row][c].value.is_empty() {
116                            return Some((row, c - 1));
117                        }
118                        last_non_empty = c;
119                    } else {
120                        return Some((row, c - 1));
121                    }
122                }
123
124                Some((row, last_non_empty))
125            }
126            Direction::Up => {
127                let mut last_non_empty = row;
128
129                for r in (1..row).rev() {
130                    if r < sheet.data.len() && col < sheet.data[r].len() {
131                        if sheet.data[r][col].value.is_empty() {
132                            return Some((r + 1, col));
133                        }
134                        last_non_empty = r;
135                    } else {
136                        return Some((r + 1, col));
137                    }
138                }
139
140                Some((last_non_empty, col))
141            }
142            Direction::Down => {
143                let mut last_non_empty = row;
144
145                for r in (row + 1)..=max_row {
146                    if r < sheet.data.len() && col < sheet.data[r].len() {
147                        if sheet.data[r][col].value.is_empty() {
148                            return Some((r - 1, col));
149                        }
150                        last_non_empty = r;
151                    } else {
152                        return Some((r - 1, col));
153                    }
154                }
155
156                Some((last_non_empty, col))
157            }
158        }
159    }
160}