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