Skip to main content

rust_excel_core/
sheet.rs

1use umya_spreadsheet::Worksheet;
2
3use crate::cell::{read_cell_value, write_cell_value};
4use crate::types::{CellInfo, CellValue, MergeRange, RangeData};
5
6/// Convert a 1-based column number to an Excel column letter (1=A, 2=B, 26=Z, 27=AA).
7fn col_num_to_letter(mut col: u32) -> String {
8    let mut result = String::new();
9    while col > 0 {
10        col -= 1;
11        result.insert(0, (b'A' + (col % 26) as u8) as char);
12        col /= 26;
13    }
14    result
15}
16
17/// Read a single cell's value.
18pub fn get_cell_value(sheet: &Worksheet, row: u32, col: u32) -> CellValue {
19    match sheet.get_cell((col, row)) {
20        Some(cell) => read_cell_value(cell),
21        None => CellValue::Empty,
22    }
23}
24
25/// Write a single cell's value.
26pub fn set_cell_value(sheet: &mut Worksheet, row: u32, col: u32, value: &CellValue) {
27    let cell = sheet.get_cell_mut((col, row));
28    write_cell_value(cell, value);
29}
30
31/// Get full cell info including formula.
32pub fn get_cell_info(sheet: &Worksheet, row: u32, col: u32) -> CellInfo {
33    let value = get_cell_value(sheet, row, col);
34    let formula = sheet
35        .get_cell((col, row))
36        .map(|c| {
37            let f = c.get_formula();
38            if f.is_empty() {
39                None
40            } else {
41                Some(f.to_string())
42            }
43        })
44        .flatten();
45
46    CellInfo {
47        row,
48        col,
49        value,
50        formula,
51    }
52}
53
54/// Read a rectangular range of cells.
55pub fn read_range(
56    sheet: &Worksheet,
57    start_row: u32,
58    start_col: u32,
59    end_row: u32,
60    end_col: u32,
61) -> RangeData {
62    let mut rows = Vec::new();
63    for r in start_row..=end_row {
64        let mut row = Vec::new();
65        for c in start_col..=end_col {
66            row.push(get_cell_value(sheet, r, c));
67        }
68        rows.push(row);
69    }
70    RangeData {
71        start_row,
72        start_col,
73        end_row,
74        end_col,
75        rows,
76    }
77}
78
79/// Write a rectangular range of cells.
80pub fn write_range(sheet: &mut Worksheet, data: &RangeData) {
81    for (ri, row) in data.rows.iter().enumerate() {
82        for (ci, val) in row.iter().enumerate() {
83            let r = data.start_row + ri as u32;
84            let c = data.start_col + ci as u32;
85            set_cell_value(sheet, r, c, val);
86        }
87    }
88}
89
90/// Insert rows at the given position.
91pub fn insert_rows(sheet: &mut Worksheet, at: u32, count: u32) {
92    for _ in 0..count {
93        sheet.insert_new_row(&at, &1);
94    }
95}
96
97/// Delete rows at the given position.
98pub fn delete_rows(sheet: &mut Worksheet, at: u32, count: u32) {
99    for _ in 0..count {
100        sheet.remove_row(&at, &1);
101    }
102}
103
104/// Insert columns at the given position.
105pub fn insert_columns(sheet: &mut Worksheet, at: u32, count: u32) {
106    for _ in 0..count {
107        sheet.insert_new_column_by_index(&at, &1);
108    }
109}
110
111/// Delete columns at the given position.
112pub fn delete_columns(sheet: &mut Worksheet, at: u32, count: u32) {
113    for _ in 0..count {
114        sheet.remove_column_by_index(&at, &1);
115    }
116}
117
118/// Set the height of a row.
119pub fn set_row_height(sheet: &mut Worksheet, row: u32, height: f64) {
120    sheet.get_row_dimension_mut(&row).set_height(height);
121}
122
123/// Set the width of a column.
124pub fn set_column_width(sheet: &mut Worksheet, col: u32, width: f64) {
125    sheet
126        .get_column_dimension_by_number_mut(&col)
127        .set_width(width);
128}
129
130/// Get the height of a row.
131pub fn get_row_height(sheet: &Worksheet, row: u32) -> f64 {
132    sheet
133        .get_row_dimension(&row)
134        .map(|r| *r.get_height())
135        .unwrap_or(15.0)
136}
137
138/// Get the width of a column.
139pub fn get_column_width(sheet: &Worksheet, col: u32) -> f64 {
140    sheet
141        .get_column_dimension_by_number(&col)
142        .map(|c| *c.get_width())
143        .unwrap_or(8.43)
144}
145
146/// Merge a range of cells. Uses Excel notation like "A1:C5".
147pub fn merge_cells(sheet: &mut Worksheet, range: &MergeRange) {
148    let range_str = format!(
149        "{}{}:{}{}",
150        col_num_to_letter(range.start_col),
151        range.start_row,
152        col_num_to_letter(range.end_col),
153        range.end_row,
154    );
155    sheet.add_merge_cells(&range_str);
156}
157
158/// Unmerge a range of cells.
159pub fn unmerge_cells(sheet: &mut Worksheet, range: &MergeRange) {
160    let range_str = format!(
161        "{}{}:{}{}",
162        col_num_to_letter(range.start_col),
163        range.start_row,
164        col_num_to_letter(range.end_col),
165        range.end_row,
166    );
167    // Remove the matching merge range from the collection
168    let merges = sheet.get_merge_cells_mut();
169    merges.retain(|r| r.get_range() != range_str);
170}
171
172/// List all merged cell ranges.
173pub fn get_merged_cells(sheet: &Worksheet) -> Vec<MergeRange> {
174    sheet
175        .get_merge_cells()
176        .iter()
177        .filter_map(|r| {
178            let start_col = r.get_coordinate_start_col()?.get_num().clone();
179            let start_row = r.get_coordinate_start_row()?.get_num().clone();
180            let end_col = r.get_coordinate_end_col()?.get_num().clone();
181            let end_row = r.get_coordinate_end_row()?.get_num().clone();
182            Some(MergeRange {
183                start_row,
184                start_col,
185                end_row,
186                end_col,
187            })
188        })
189        .collect()
190}