Files
IronCalc/base/src/styles.rs
Varun Hegde 2b03b3e3b9 Error Handling of public Set functions (#88)
What are we trying to achieve ?

++ Currently all the major public set functions is panic prone and does not handle and return error. This PR tries to address to all those functions.

What major errors that could happen in these functions ?

++ All the functions which are being made as error safe is being tested against invalid sheet, row and column values, which could given by user

What are the list of functions whose return type has been altered ?

**base/src/model.rs**
1. update_cell_with_text
2. update_cell_with_bool
3. update_cell_with_number
4. set_user_input
5. get_cell_style_index
6. get_style_for_cell
7. set_cell_with_string

++> New functions being added

1. set_cell_with_boolean
2. set_cell_with_number

**base/src/styles.rs**

1. get_style_with_quote_prefix
3. get_style_with_format
4. get_style_without_quote_prefix
5. get_style

**base/src/worksheet.rs**

1. update_cell
2. set_cell_style
3. set_cell_with_formula
4. set_cell_with_number
6. set_cell_with_string
8. set_cell_with_boolean
9. set_cell_with_error
10. cell_clear_contents
11. cell_clear_contents_with_style

++> Above is the comprehensive list of all functions being ( most are public, some are private ) altered for better error handling. As a side effect of changing function signature, there are many changes being done to other functions ( mostly adding "?" to enable to error propagation further )
2024-09-14 17:37:31 +02:00

293 lines
9.6 KiB
Rust

use crate::{
model::Model,
number_format::{get_default_num_fmt_id, get_new_num_fmt_index, get_num_fmt},
types::{Border, CellStyles, CellXfs, Fill, Font, NumFmt, Style, Styles},
};
// TODO: Move Styles and all related types from crate::types here
// Not doing it right now to not have conflicts with exporter branch
impl Styles {
fn get_font_index(&self, font: &Font) -> Option<i32> {
for (font_index, item) in self.fonts.iter().enumerate() {
if item == font {
return Some(font_index as i32);
}
}
None
}
fn get_fill_index(&self, fill: &Fill) -> Option<i32> {
for (fill_index, item) in self.fills.iter().enumerate() {
if item == fill {
return Some(fill_index as i32);
}
}
None
}
fn get_border_index(&self, border: &Border) -> Option<i32> {
for (border_index, item) in self.borders.iter().enumerate() {
if item == border {
return Some(border_index as i32);
}
}
None
}
fn get_num_fmt_index(&self, format_code: &str) -> Option<i32> {
if let Some(index) = get_default_num_fmt_id(format_code) {
return Some(index);
}
for item in self.num_fmts.iter() {
if item.format_code == format_code {
return Some(item.num_fmt_id);
}
}
None
}
pub fn create_new_style(&mut self, style: &Style) -> i32 {
let font = &style.font;
let font_id = if let Some(index) = self.get_font_index(font) {
index
} else {
self.fonts.push(font.clone());
self.fonts.len() as i32 - 1
};
let fill = &style.fill;
let fill_id = if let Some(index) = self.get_fill_index(fill) {
index
} else {
self.fills.push(fill.clone());
self.fills.len() as i32 - 1
};
let border = &style.border;
let border_id = if let Some(index) = self.get_border_index(border) {
index
} else {
self.borders.push(border.clone());
self.borders.len() as i32 - 1
};
let num_fmt = &style.num_fmt;
let num_fmt_id;
if let Some(index) = self.get_num_fmt_index(num_fmt) {
num_fmt_id = index;
} else {
num_fmt_id = get_new_num_fmt_index(&self.num_fmts);
self.num_fmts.push(NumFmt {
format_code: num_fmt.to_string(),
num_fmt_id,
});
}
self.cell_xfs.push(CellXfs {
xf_id: 0,
num_fmt_id,
font_id,
fill_id,
border_id,
apply_number_format: false,
apply_border: false,
apply_alignment: false,
apply_protection: false,
apply_font: false,
apply_fill: false,
quote_prefix: style.quote_prefix,
alignment: style.alignment.clone(),
});
self.cell_xfs.len() as i32 - 1
}
pub fn get_style_index(&self, style: &Style) -> Option<i32> {
for (index, cell_xf) in self.cell_xfs.iter().enumerate() {
let border_id = cell_xf.border_id as usize;
let fill_id = cell_xf.fill_id as usize;
let font_id = cell_xf.font_id as usize;
let num_fmt_id = cell_xf.num_fmt_id;
let quote_prefix = cell_xf.quote_prefix;
if style
== &(Style {
alignment: cell_xf.alignment.clone(),
num_fmt: get_num_fmt(num_fmt_id, &self.num_fmts),
fill: self.fills[fill_id].clone(),
font: self.fonts[font_id].clone(),
border: self.borders[border_id].clone(),
quote_prefix,
})
{
return Some(index as i32);
}
}
None
}
pub(crate) fn get_style_index_or_create(&mut self, style: &Style) -> i32 {
// Check if style exist. If so sets style cell number to that otherwise create a new style.
if let Some(index) = self.get_style_index(style) {
index
} else {
self.create_new_style(style)
}
}
/// Adds a named cell style from an existing index
/// Fails if the named style already exists or if there is not a style with that index
pub fn add_named_cell_style(
&mut self,
style_name: &str,
style_index: i32,
) -> Result<(), String> {
if self.get_style_index_by_name(style_name).is_ok() {
return Err("A style with that name already exists".to_string());
}
if self.cell_xfs.len() < style_index as usize {
return Err("There is no style with that index".to_string());
}
let cell_style = CellStyles {
name: style_name.to_string(),
xf_id: style_index,
builtin_id: 0,
};
self.cell_styles.push(cell_style);
Ok(())
}
// Returns the index of the style or fails.
// NB: this method is case sensitive
pub fn get_style_index_by_name(&self, style_name: &str) -> Result<i32, String> {
for cell_style in &self.cell_styles {
if cell_style.name == style_name {
return Ok(cell_style.xf_id);
}
}
Err(format!("Style '{}' not found", style_name))
}
pub fn create_named_style(&mut self, style_name: &str, style: &Style) -> Result<(), String> {
let style_index = self.create_new_style(style);
self.add_named_cell_style(style_name, style_index)
}
pub(crate) fn get_style_with_quote_prefix(&mut self, index: i32) -> Result<i32, String> {
let mut style = self.get_style(index)?;
style.quote_prefix = true;
Ok(self.get_style_index_or_create(&style))
}
pub(crate) fn get_style_with_format(
&mut self,
index: i32,
num_fmt: &str,
) -> Result<i32, String> {
let mut style = self.get_style(index)?;
style.num_fmt = num_fmt.to_string();
Ok(self.get_style_index_or_create(&style))
}
pub(crate) fn get_style_without_quote_prefix(&mut self, index: i32) -> Result<i32, String> {
let mut style = self.get_style(index)?;
style.quote_prefix = false;
Ok(self.get_style_index_or_create(&style))
}
pub(crate) fn style_is_quote_prefix(&self, index: i32) -> bool {
let cell_xf = &self.cell_xfs[index as usize];
cell_xf.quote_prefix
}
pub(crate) fn get_style(&self, index: i32) -> Result<Style, String> {
let cell_xf = &self
.cell_xfs
.get(index as usize)
.ok_or("Invalid index provided".to_string())?;
let border_id = cell_xf.border_id as usize;
let fill_id = cell_xf.fill_id as usize;
let font_id = cell_xf.font_id as usize;
let num_fmt_id = cell_xf.num_fmt_id;
let quote_prefix = cell_xf.quote_prefix;
let alignment = cell_xf.alignment.clone();
Ok(Style {
alignment,
num_fmt: get_num_fmt(num_fmt_id, &self.num_fmts),
fill: self.fills[fill_id].clone(),
font: self.fonts[font_id].clone(),
border: self.borders[border_id].clone(),
quote_prefix,
})
}
}
// TODO: Try to find a better spot for styles setters
impl Model {
pub fn set_cell_style(
&mut self,
sheet: u32,
row: i32,
column: i32,
style: &Style,
) -> Result<(), String> {
let style_index = self.workbook.styles.get_style_index_or_create(style);
self.workbook
.worksheet_mut(sheet)?
.set_cell_style(row, column, style_index)
}
pub fn copy_cell_style(
&mut self,
source_cell: (u32, i32, i32),
destination_cell: (u32, i32, i32),
) -> Result<(), String> {
let source_style_index = self
.workbook
.worksheet(source_cell.0)?
.get_style(source_cell.1, source_cell.2);
self.workbook
.worksheet_mut(destination_cell.0)?
.set_cell_style(destination_cell.1, destination_cell.2, source_style_index)
}
/// Sets the style "style_name" in cell
pub fn set_cell_style_by_name(
&mut self,
sheet: u32,
row: i32,
column: i32,
style_name: &str,
) -> Result<(), String> {
let style_index = self.workbook.styles.get_style_index_by_name(style_name)?;
self.workbook
.worksheet_mut(sheet)?
.set_cell_style(row, column, style_index)
}
pub fn set_sheet_style(&mut self, sheet: u32, style_name: &str) -> Result<(), String> {
let style_index = self.workbook.styles.get_style_index_by_name(style_name)?;
self.workbook.worksheet_mut(sheet)?.set_style(style_index)?;
Ok(())
}
pub fn set_sheet_row_style(
&mut self,
sheet: u32,
row: i32,
style_name: &str,
) -> Result<(), String> {
let style_index = self.workbook.styles.get_style_index_by_name(style_name)?;
self.workbook
.worksheet_mut(sheet)?
.set_row_style(row, style_index)?;
Ok(())
}
pub fn set_sheet_column_style(
&mut self,
sheet: u32,
column: i32,
style_name: &str,
) -> Result<(), String> {
let style_index = self.workbook.styles.get_style_index_by_name(style_name)?;
self.workbook
.worksheet_mut(sheet)?
.set_column_style(column, style_index)?;
Ok(())
}
}