use std::iter::successors;
use crate::helper::utils::compile_regex;
fn index_to_alpha(index: u32) -> String {
const BASE_CHAR_CODE: u32 = 'A' as u32;
assert!(index >= 1, "Index cannot be less than one.");
successors(Some(index - 1), |index| match index / 26u32 {
0 => None,
n => Some(n - 1),
})
.map(|v| BASE_CHAR_CODE + (v % 26))
.collect::<Vec<u32>>()
.into_iter()
.rev()
.map(|v| char::from_u32(v).unwrap())
.collect()
}
fn alpha_to_index<S>(alpha: S) -> u32
where
S: AsRef<str>,
{
const BASE_CHAR_CODE: u32 = 'A' as u32;
const POSITIONAL_CONSTANTS: [u32; 3] = [1, 26, 676];
alpha
.as_ref()
.to_uppercase()
.chars()
.rev()
.enumerate()
.map(|(index, v)| {
let vn = (v as u32 - BASE_CHAR_CODE) + 1;
POSITIONAL_CONSTANTS[index] * vn
})
.sum::<u32>()
}
#[inline]
pub fn column_index_from_string<S: AsRef<str>>(column: S) -> u32 {
let column_c = column.as_ref();
if column_c == "0" {
return 0;
}
alpha_to_index(column_c)
}
#[inline]
#[must_use]
pub fn string_from_column_index(column_index: u32) -> String {
assert!(column_index >= 1u32, "Column number starts from 1.");
index_to_alpha(column_index)
}
pub fn index_from_coordinate<T>(coordinate: T) -> CellIndex
where
T: AsRef<str>,
{
let re = compile_regex!(r"^((\$)?([A-Z]{1,3}))?((\$)?([0-9]+))?$");
re.captures(coordinate.as_ref())
.ok()
.flatten()
.map(|v| {
let col = v.get(3).map(|v| alpha_to_index(v.as_str())); let row = v.get(6).and_then(|v| v.as_str().parse::<u32>().ok());
let col_lock_flg = col.map(|_col| {
v.get(2).is_some() });
let row_lock_flg = row.map(|_row| {
v.get(5).is_some() });
(col, row, col_lock_flg, row_lock_flg)
})
.unwrap_or_default()
}
#[inline]
#[must_use]
pub fn coordinate_from_index(col: u32, row: u32) -> String {
format!("{}{}", string_from_column_index(col), row)
}
#[must_use]
pub fn coordinate_from_index_with_lock(
col: u32,
row: u32,
is_lock_col: bool,
is_lock_row: bool,
) -> String {
format!(
"{}{}{}{}",
if is_lock_col { "$" } else { "" },
string_from_column_index(col),
if is_lock_row { "$" } else { "" },
row
)
}
#[inline]
pub(crate) fn adjustment_insert_coordinate(num: u32, root_num: u32, offset_num: u32) -> u32 {
if num >= root_num && offset_num != 0 {
num.saturating_add(offset_num)
} else {
num
}
}
#[inline]
pub(crate) fn adjustment_remove_coordinate(num: u32, root_num: u32, offset_num: u32) -> u32 {
if num >= root_num && offset_num != 0 {
num.saturating_sub(offset_num)
} else {
num
}
}
#[inline]
pub(crate) fn is_remove_coordinate(num: u32, root_num: u32, offset_num: u32) -> bool {
if root_num != 0 && offset_num != 0 {
return num >= root_num && num < (root_num + offset_num);
}
false
}
pub type CellIndex = (Option<u32>, Option<u32>, Option<bool>, Option<bool>);
#[derive(Clone, Debug)]
pub struct CellCoordinates {
pub row: u32, pub col: u32, }
impl CellCoordinates {
#[inline]
fn new(col: u32, row: u32) -> Self {
CellCoordinates { row, col }
}
}
impl From<(u32, u32)> for CellCoordinates {
#[inline]
fn from(value: (u32, u32)) -> Self {
CellCoordinates::new(value.0, value.1)
}
}
impl From<String> for CellCoordinates {
#[inline]
fn from(value: String) -> Self {
value.as_str().into()
}
}
impl From<&str> for CellCoordinates {
#[inline]
fn from(value: &str) -> Self {
let (col, row, ..) = index_from_coordinate(value.to_uppercase());
CellCoordinates::new(col.unwrap(), row.unwrap())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn column_index_from_string_1() {
assert_eq!(column_index_from_string("A"), 1);
assert_eq!(column_index_from_string("B"), 2);
assert_eq!(column_index_from_string("Z"), 26);
assert_eq!(column_index_from_string("AA"), 27);
assert_eq!(column_index_from_string("AB"), 28);
assert_eq!(column_index_from_string("BA"), 53);
assert_eq!(column_index_from_string("ZZ"), 702);
assert_eq!(column_index_from_string("AAA"), 703);
assert_eq!(column_index_from_string("LAV"), 8160);
assert_eq!(column_index_from_string("XFD"), 16384); }
#[test]
fn string_from_column_index_1() {
assert_eq!(string_from_column_index(1), String::from("A"));
assert_eq!(string_from_column_index(26), String::from("Z"));
assert_eq!(string_from_column_index(27), String::from("AA"));
assert_eq!(string_from_column_index(28), String::from("AB"));
assert_eq!(string_from_column_index(53), String::from("BA"));
assert_eq!(string_from_column_index(702), String::from("ZZ"));
assert_eq!(string_from_column_index(703), String::from("AAA"));
assert_eq!(string_from_column_index(8160), String::from("LAV"));
assert_eq!(string_from_column_index(16384), String::from("XFD"));
}
#[test]
fn index_from_coordinate_1() {
assert_eq!(
index_from_coordinate("$A$4"),
(Some(1), Some(4), Some(true), Some(true))
);
assert_eq!(
index_from_coordinate("$A4"),
(Some(1), Some(4), Some(true), Some(false))
);
assert_eq!(
index_from_coordinate("A4"),
(Some(1), Some(4), Some(false), Some(false))
);
assert_eq!(
index_from_coordinate("Z91"),
(Some(26), Some(91), Some(false), Some(false))
);
assert_eq!(
index_from_coordinate("AA91"),
(Some(27), Some(91), Some(false), Some(false))
);
assert_eq!(
index_from_coordinate("AA$91"),
(Some(27), Some(91), Some(false), Some(true))
);
assert_eq!(
index_from_coordinate("$AA91"),
(Some(27), Some(91), Some(true), Some(false))
);
assert_eq!(
index_from_coordinate("$AA$91"),
(Some(27), Some(91), Some(true), Some(true))
);
assert_eq!(
index_from_coordinate("A"),
(Some(1), None, Some(false), None)
);
assert_eq!(
index_from_coordinate("$A"),
(Some(1), None, Some(true), None)
);
assert_eq!(
index_from_coordinate("5"),
(None, Some(5), None, Some(false))
);
assert_eq!(
index_from_coordinate("$5"),
(None, Some(5), None, Some(true))
);
assert_eq!(
index_from_coordinate("Table1[[#This Row]"),
(None, None, None, None)
);
}
}