1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/// Convert a zero indexed column cell reference to a string like `"A"`.
///
/// Utility function to convert a zero based column reference to a string
/// representation. This can be useful when constructing ranges for formulas.
///
/// # Examples:
///
/// ```
/// use rsheet_lib::cells::column_number_to_name;
///
/// assert_eq!(column_number_to_name(0), "A");
/// assert_eq!(column_number_to_name(1), "B");
/// assert_eq!(column_number_to_name(702), "AAA");
/// ```
/// 
/// Source: [rust_xlsxwriter].
/// 
/// [rust_xlsxwriter]: https://docs.rs/rust_xlsxwriter/latest/src/rust_xlsxwriter/utility.rs.html#97-105
///
pub fn column_number_to_name(col_num: u32) -> String {
    let mut col_name_reversed = String::new();

    let mut col_num = col_num + 1;

    while col_num > 0 {
        // Set remainder from 1 .. 26
        let mut remainder = col_num % 26;

        if remainder == 0 {
            remainder = 26;
        }

        // Convert the remainder to a character.
        let col_letter = char::from_u32('A' as u32 - 1 + remainder).unwrap();

        // Accumulate the column letters, right to left.
        col_name_reversed.push(col_letter);

        // Get the next order of magnitude.
        col_num = (col_num - 1) / 26;
    }

    col_name_reversed.chars().rev().collect()
}

/// Convert a column string such as `"A"` to a zero indexed column reference.
///
/// Utility function to convert a column string representation to a zero based
/// column reference.
///
/// # Examples:
///
/// ```
/// use rsheet_lib::cells::column_name_to_number;
///
/// assert_eq!(column_name_to_number("A"), 0);
/// assert_eq!(column_name_to_number("B"), 1);
/// assert_eq!(column_name_to_number("AAA"), 702);
/// ```
///
pub fn column_name_to_number(column: &str) -> u32 {
    assert!(column.is_ascii());

    let mut col_num = 0;

    for char in column.chars() {
        col_num = (col_num * 26) + (char as u32 - 'A' as u32 + 1);
    }

    col_num - 1
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_column_number_to_name() {
        assert_eq!(column_number_to_name(0), "A");
        assert_eq!(column_number_to_name(1), "B");
        assert_eq!(column_number_to_name(25), "Z");
        assert_eq!(column_number_to_name(26), "AA");
        assert_eq!(column_number_to_name(27), "AB");
        assert_eq!(column_number_to_name(51), "AZ");
        assert_eq!(column_number_to_name(52), "BA");
        assert_eq!(column_number_to_name(53), "BB");
        assert_eq!(column_number_to_name(701), "ZZ");
        assert_eq!(column_number_to_name(702), "AAA");
        assert_eq!(column_number_to_name(703), "AAB");
    }

    #[test]
    fn test_column_name_to_number() {
        assert_eq!(column_name_to_number("A"), 0);
        assert_eq!(column_name_to_number("B"), 1);
        assert_eq!(column_name_to_number("Z"), 25);
        assert_eq!(column_name_to_number("AA"), 26);
        assert_eq!(column_name_to_number("AB"), 27);
        assert_eq!(column_name_to_number("AZ"), 51);
        assert_eq!(column_name_to_number("BA"), 52);
        assert_eq!(column_name_to_number("BB"), 53);
        assert_eq!(column_name_to_number("ZZ"), 701);
        assert_eq!(column_name_to_number("AAA"), 702);
        assert_eq!(column_name_to_number("AAB"), 703);
    }
}