1use alloc::format;
2use alloc::string::String;
3use alloc::string::ToString;
4
5use crate::{SelectionKind, Table, TableGrid};
6
7const SPAN_COL_A_VALUE: usize = 'A' as usize;
8pub const NO_END: usize = usize::MAX; pub fn span_a2i(a: &str) -> usize {
13 a.chars().fold(0, |acc, c| {
14 let cu = c.to_ascii_uppercase();
15 acc * 26 + (cu as usize - SPAN_COL_A_VALUE + 1)
16 }) - 1
17}
18
19pub fn col_index_to_letter(mut col: usize) -> String {
22 let mut s = String::new();
23 col += 1; while col > 0 {
25 let rem = (col - 1) % 26;
26 s.push((b'A' + rem as u8) as char);
27 col = (col - 1) / 26;
28 }
29 s.chars().rev().collect()
30}
31
32#[cfg_attr(feature = "tsify", derive(tsify::Tsify))]
33#[cfg_attr(feature = "tsify", tsify(from_wasm_abi))]
34#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
35#[derive(Debug, Clone, PartialEq)]
36pub enum SpanKey {
37 Span(Span),
38 None,
39 Un64(usize),
40 Tuple2(Option<usize>, Option<usize>),
41 Tuple4(Option<usize>, Option<usize>, Option<usize>, Option<usize>),
42 TuplePair(
43 (Option<usize>, Option<usize>),
44 (Option<usize>, Option<usize>),
45 ),
46 Str(String),
47}
48
49#[cfg_attr(feature = "tsify", derive(tsify::Tsify))]
50#[cfg_attr(feature = "tsify", tsify(from_wasm_abi))]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52#[derive(Debug, Copy, Clone, PartialEq)]
53pub struct Span {
54 pub row: usize,
55 pub end_row: usize, pub col: usize,
57 pub end_col: usize, }
59
60impl Span {
61 pub fn new(row: usize, end_row: usize, col: usize, end_col: usize) -> Self {
62 Span {
63 row,
64 end_row,
65 col,
66 end_col,
67 }
68 }
69
70 pub fn kind(&self) -> SelectionKind {
75 match (self.end_row == NO_END, self.end_col == NO_END) {
76 (true, false) => SelectionKind::Rows,
77 (false, true) => SelectionKind::Cols,
78 _ => SelectionKind::Cells,
79 }
80 }
81
82 pub fn rows(&self, grid: &TableGrid) -> core::ops::Range<usize> {
84 let start = self.row;
85 if self.end_row != NO_END {
86 start..self.end_row + 1
87 } else {
88 start..Table::acr_total_rows(&grid.index.cells)
89 }
90 }
91
92 pub fn rows_start_end(&self, grid: &TableGrid) -> (usize, usize) {
94 let start = self.row;
95 if self.end_row != NO_END {
96 (start, self.end_row)
97 } else {
98 (
99 start,
100 Table::acr_total_rows(&grid.index.cells).saturating_sub(1),
101 )
102 }
103 }
104
105 pub fn cols(&self, grid: &TableGrid) -> core::ops::Range<usize> {
107 let start = self.col;
108 if self.end_col != NO_END {
109 start..self.end_col + 1
110 } else {
111 start..Table::acr_total_cols(&grid.header.cells)
112 }
113 }
114
115 pub fn cols_start_end(&self, grid: &TableGrid) -> (usize, usize) {
117 let start = self.col;
118 if self.end_col != NO_END {
119 (start, self.end_col)
120 } else {
121 (
122 start,
123 Table::acr_total_cols(&grid.header.cells).saturating_sub(1),
124 )
125 }
126 }
127
128 pub fn max_row(&self, grid: &TableGrid) -> usize {
130 if self.end_row != NO_END {
131 self.end_row
132 } else {
133 Table::acr_total_rows(&grid.index.cells).saturating_sub(1)
134 }
135 }
136
137 pub fn max_col(&self, grid: &TableGrid) -> usize {
139 if self.end_col != NO_END {
140 self.end_col
141 } else {
142 Table::acr_total_cols(&grid.header.cells).saturating_sub(1)
143 }
144 }
145
146 pub fn to_string(&self) -> String {
149 if self.row == 0 && self.end_row == NO_END && self.col == 0 && self.end_col == NO_END {
150 return ":".to_string();
151 }
152
153 let col_s = col_index_to_letter(self.col);
154 let col_e = if self.end_col != NO_END {
155 col_index_to_letter(self.end_col)
156 } else {
157 String::new()
158 };
159 let row_s = (self.row + 1).to_string();
160 let row_e = if self.end_row != NO_END {
161 (self.end_row + 1).to_string()
162 } else {
163 String::new()
164 };
165
166 if self.row == 0 && self.end_row == NO_END {
168 if self.col == self.end_col && self.end_col != NO_END {
169 col_s
170 } else if self.end_col == NO_END {
171 format!("{}:", col_s)
172 } else if self.col == 0 {
173 format!(":{}", col_e)
174 } else {
175 format!("{}:{}", col_s, col_e)
176 }
177 }
178 else if self.col == 0 && self.end_col == NO_END {
180 if self.row == self.end_row && self.end_row != NO_END {
181 row_s
182 } else if self.end_row == NO_END {
183 format!("{}:", row_s)
184 } else if self.row == 0 {
185 format!(":{}", row_e)
186 } else {
187 format!("{}:{}", row_s, row_e)
188 }
189 }
190 else {
192 let start = format!("{}{}", col_s, row_s);
193 if self.row == self.end_row && self.col == self.end_col && self.end_col != NO_END {
194 return start; }
196
197 let end = if self.end_row == NO_END && self.end_col == NO_END {
198 String::new()
199 } else if self.end_row == NO_END {
200 col_e
201 } else if self.end_col == NO_END {
202 row_e
203 } else {
204 format!("{}{}", col_e, row_e)
205 };
206
207 if end.is_empty() {
208 format!("{}:", start)
209 } else {
210 format!("{}:{}", start, end)
211 }
212 }
213 }
214}
215
216impl core::fmt::Display for Span {
217 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
218 write!(f, "{}", self.to_string())
219 }
220}
221
222impl Default for SpanKey {
223 fn default() -> Self {
224 SpanKey::None
225 }
226}
227
228impl From<usize> for SpanKey {
229 fn from(row: usize) -> Self {
230 SpanKey::Un64(row)
231 }
232}
233
234impl From<(Option<usize>, Option<usize>)> for SpanKey {
235 fn from((r, c): (Option<usize>, Option<usize>)) -> Self {
236 SpanKey::Tuple2(r, c)
237 }
238}
239
240impl From<(usize, usize)> for SpanKey {
241 fn from((r, c): (usize, usize)) -> Self {
242 SpanKey::Tuple2(Some(r), Some(c))
243 }
244}
245
246impl From<(Option<usize>, Option<usize>, Option<usize>, Option<usize>)> for SpanKey {
247 fn from(
248 (fr, ur, fc, uc): (Option<usize>, Option<usize>, Option<usize>, Option<usize>),
249 ) -> Self {
250 SpanKey::Tuple4(fr, ur, fc, uc)
251 }
252}
253
254impl
255 From<(
256 (Option<usize>, Option<usize>),
257 (Option<usize>, Option<usize>),
258 )> for SpanKey
259{
260 fn from(
261 ((fr, ur), (fc, uc)): (
262 (Option<usize>, Option<usize>),
263 (Option<usize>, Option<usize>),
264 ),
265 ) -> Self {
266 SpanKey::TuplePair((fr, ur), (fc, uc))
267 }
268}
269
270impl From<&str> for SpanKey {
271 fn from(s: &str) -> Self {
272 SpanKey::Str(s.to_string())
273 }
274}
275
276impl From<String> for SpanKey {
277 fn from(s: String) -> Self {
278 SpanKey::Str(s)
279 }
280}
281
282#[derive(Debug, Clone, Copy, PartialEq, Eq)]
287enum Part {
288 Empty,
289 Row(usize),
290 Col(usize),
291 Cell { col: usize, row: usize },
292}
293
294fn classify_part(p: &str) -> Option<Part> {
296 if p.is_empty() {
297 return Some(Part::Empty);
298 }
299
300 let bytes = p.as_bytes();
301 let first = bytes[0];
302
303 if first.is_ascii_digit() {
304 for &b in bytes {
305 if !b.is_ascii_digit() {
306 return None;
307 }
308 }
309 if let Ok(n) = p.parse::<usize>() {
310 return Some(Part::Row(n.saturating_sub(1)));
311 }
312 return None;
313 }
314
315 if first.is_ascii_alphabetic() {
316 let mut letter_end = 0usize;
317 for (i, &b) in bytes.iter().enumerate() {
318 if b.is_ascii_alphabetic() {
319 letter_end = i + 1;
320 } else if b.is_ascii_digit() {
321 break;
322 } else {
323 return None;
324 }
325 }
326
327 if letter_end == 0 {
328 return None;
329 }
330
331 let letters = &p[..letter_end];
332 let rest = &p[letter_end..];
333
334 if rest.is_empty() {
335 return Some(Part::Col(span_a2i(letters)));
336 }
337
338 for &b in rest.as_bytes() {
339 if !b.is_ascii_digit() {
340 return None;
341 }
342 }
343
344 if let Ok(rn) = rest.parse::<usize>() {
345 return Some(Part::Cell {
346 col: span_a2i(letters),
347 row: rn.saturating_sub(1),
348 });
349 }
350 return None;
351 }
352
353 None
354}
355
356impl Table {
357 pub fn cr_key_to_span(&self, key: SpanKey) -> Result<Span, String> {
358 match key {
359 SpanKey::Span(coords) => Ok(coords),
360 SpanKey::None => Ok(Span {
361 row: 0,
362 end_row: NO_END,
363 col: 0,
364 end_col: NO_END,
365 }),
366 SpanKey::Un64(row) => Ok(Span {
367 row,
368 end_row: row,
369 col: 0,
370 end_col: NO_END,
371 }),
372 SpanKey::Tuple2(r, c) => Ok(Span {
373 row: r.unwrap_or(0),
374 end_row: r.unwrap_or(NO_END),
375 col: c.unwrap_or(0),
376 end_col: c.unwrap_or(NO_END),
377 }),
378 SpanKey::Tuple4(fr, ur, fc, uc) => Ok(Span {
379 row: fr.unwrap_or(0),
380 end_row: ur.unwrap_or(NO_END),
381 col: fc.unwrap_or(0),
382 end_col: uc.unwrap_or(NO_END),
383 }),
384 SpanKey::TuplePair((fr, ur), (fc, uc)) => Ok(Span {
385 row: fr.unwrap_or(0),
386 end_row: ur.unwrap_or(NO_END),
387 col: fc.unwrap_or(0),
388 end_col: uc.unwrap_or(NO_END),
389 }),
390 SpanKey::Str(s) => self.parse_string_to_span(&s),
391 }
392 }
393
394 #[inline]
395 fn parse_string_to_span(&self, s: &str) -> Result<Span, String> {
396 if s.is_empty() {
397 return Ok(Span {
398 row: 0,
399 end_row: NO_END,
400 col: 0,
401 end_col: NO_END,
402 });
403 }
404
405 if s == ":" {
406 return Ok(Span {
407 row: 0,
408 end_row: NO_END,
409 col: 0,
410 end_col: NO_END,
411 });
412 }
413
414 if let Some((left_str, right_str)) = s.split_once(':') {
415 let left = classify_part(left_str)
416 .ok_or_else(|| format!("'{}' could not be converted to span.", s))?;
417 let right = classify_part(right_str)
418 .ok_or_else(|| format!("'{}' could not be converted to span.", s))?;
419
420 match (left, right) {
421 (Part::Row(fr), Part::Row(ur)) => Ok(Span {
422 row: fr,
423 end_row: ur,
424 col: 0,
425 end_col: NO_END,
426 }),
427 (Part::Col(fc), Part::Col(uc)) => Ok(Span {
428 row: 0,
429 end_row: NO_END,
430 col: fc,
431 end_col: uc,
432 }),
433 (Part::Cell { col: fc, row: fr }, Part::Cell { col: uc, row: ur }) => Ok(Span {
434 row: fr,
435 end_row: ur,
436 col: fc,
437 end_col: uc,
438 }),
439 (Part::Row(fr), Part::Empty) => Ok(Span {
440 row: fr,
441 end_row: NO_END,
442 col: 0,
443 end_col: NO_END,
444 }),
445 (Part::Empty, Part::Row(ur)) => Ok(Span {
446 row: 0,
447 end_row: ur,
448 col: 0,
449 end_col: NO_END,
450 }),
451 (Part::Col(fc), Part::Empty) => Ok(Span {
452 row: 0,
453 end_row: NO_END,
454 col: fc,
455 end_col: NO_END,
456 }),
457 (Part::Empty, Part::Col(uc)) => Ok(Span {
458 row: 0,
459 end_row: NO_END,
460 col: 0,
461 end_col: uc,
462 }),
463 (Part::Cell { col: fc, row: fr }, Part::Empty) => Ok(Span {
464 row: fr,
465 end_row: NO_END,
466 col: fc,
467 end_col: NO_END,
468 }),
469 (Part::Empty, Part::Cell { col: uc, row: ur }) => Ok(Span {
470 row: 0,
471 end_row: ur,
472 col: 0,
473 end_col: uc,
474 }),
475 (Part::Cell { col: fc, row: fr }, Part::Col(uc)) => Ok(Span {
476 row: fr,
477 end_row: NO_END,
478 col: fc,
479 end_col: uc,
480 }),
481 (Part::Cell { col: fc, row: fr }, Part::Row(ur)) => Ok(Span {
482 row: fr,
483 end_row: ur,
484 col: fc,
485 end_col: NO_END,
486 }),
487 _ => Err(format!("'{}' could not be converted to span.", s)),
488 }
489 } else {
490 match classify_part(s) {
491 Some(Part::Row(r)) => Ok(Span {
492 row: r,
493 end_row: r,
494 col: 0,
495 end_col: NO_END,
496 }),
497 Some(Part::Col(c)) => Ok(Span {
498 row: 0,
499 end_row: NO_END,
500 col: c,
501 end_col: c,
502 }),
503 Some(Part::Cell { col: c, row: r }) => Ok(Span {
504 row: r,
505 end_row: r,
506 col: c,
507 end_col: c,
508 }),
509 _ => Err(format!("'{}' could not be converted to span.", s)),
510 }
511 }
512 }
513
514 pub fn cr_span_to_string(&self, span: &Span) -> String {
516 span.to_string()
517 }
518}