FIX: Forbid unwrap, expect and panic in the base code

This commit is contained in:
Nicolás Hatcher
2024-11-16 14:18:12 +01:00
committed by Nicolás Hatcher Andrés
parent bdd2c8fe04
commit 49ae2d8915
43 changed files with 341 additions and 128 deletions

View File

@@ -1,7 +1,7 @@
.PHONY: lint .PHONY: lint
lint: lint:
cargo fmt -- --check cargo fmt -- --check
cargo clippy --all-targets --all-features cargo clippy --all-targets --all-features -- -W clippy::unwrap_used -W clippy::expect_used -W clippy::panic -D warnings
cd webapp && npm install && npm run check cd webapp && npm install && npm run check
.PHONY: format .PHONY: format

View File

@@ -26,6 +26,7 @@ pub struct SetCellValue {
} }
impl Model { impl Model {
#[allow(clippy::expect_used)]
pub(crate) fn shift_cell_formula( pub(crate) fn shift_cell_formula(
&mut self, &mut self,
sheet: u32, sheet: u32,
@@ -57,6 +58,7 @@ impl Model {
} }
} }
#[allow(clippy::expect_used)]
pub fn forward_references( pub fn forward_references(
&mut self, &mut self,
source_area: &Area, source_area: &Area,

View File

@@ -1,3 +1,5 @@
#![allow(clippy::expect_used)]
use crate::expressions::{ use crate::expressions::{
lexer::util::get_tokens, lexer::util::get_tokens,
token::{OpCompare, OpSum, TokenType}, token::{OpCompare, OpSum, TokenType},

View File

@@ -52,7 +52,9 @@ pub fn get_tokens(formula: &str) -> Vec<MarkedToken> {
let mut lexer = Lexer::new( let mut lexer = Lexer::new(
formula, formula,
LexerMode::A1, LexerMode::A1,
#[allow(clippy::expect_used)]
get_locale("en").expect(""), get_locale("en").expect(""),
#[allow(clippy::expect_used)]
get_language("en").expect(""), get_language("en").expect(""),
); );
let mut start = lexer.get_position(); let mut start = lexer.get_position();

View File

@@ -63,7 +63,9 @@ pub(crate) fn parse_range(formula: &str) -> Result<(i32, i32, i32, i32), String>
let mut lexer = lexer::Lexer::new( let mut lexer = lexer::Lexer::new(
formula, formula,
lexer::LexerMode::A1, lexer::LexerMode::A1,
#[allow(clippy::expect_used)]
get_locale("en").expect(""), get_locale("en").expect(""),
#[allow(clippy::expect_used)]
get_language("en").expect(""), get_language("en").expect(""),
); );
if let TokenType::Range { if let TokenType::Range {
@@ -202,7 +204,9 @@ impl Parser {
let lexer = lexer::Lexer::new( let lexer = lexer::Lexer::new(
"", "",
lexer::LexerMode::A1, lexer::LexerMode::A1,
#[allow(clippy::expect_used)]
get_locale("en").expect(""), get_locale("en").expect(""),
#[allow(clippy::expect_used)]
get_language("en").expect(""), get_language("en").expect(""),
); );
Parser { Parser {
@@ -675,14 +679,23 @@ impl Parser {
} }
}; };
// table-name => table // table-name => table
let table = self.tables.get(&table_name).unwrap_or_else(|| { let table = match self.tables.get(&table_name) {
panic!( Some(t) => t,
"Table not found: '{table_name}' at '{}!{}{}'", None => {
context.sheet, let message = format!(
number_to_column(context.column).expect(""), "Table not found: '{table_name}' at '{}!{}{}'",
context.row context.sheet,
) number_to_column(context.column)
}); .unwrap_or(format!("{}", context.column)),
context.row
);
return Node::ParseErrorKind {
formula: self.lexer.get_formula(),
position: 0,
message,
};
}
};
let table_sheet_index = match self.get_sheet_index_by_name(&table.sheet_name) { let table_sheet_index = match self.get_sheet_index_by_name(&table.sheet_name) {
Some(i) => i, Some(i) => i,
None => { None => {
@@ -701,6 +714,7 @@ impl Parser {
}; };
// context must be with tables.reference // context must be with tables.reference
#[allow(clippy::expect_used)]
let (column_start, mut row_start, column_end, mut row_end) = let (column_start, mut row_start, column_end, mut row_end) =
parse_range(&table.reference).expect("Failed parsing range"); parse_range(&table.reference).expect("Failed parsing range");

View File

@@ -1,3 +1,5 @@
#![allow(clippy::panic)]
use std::collections::HashMap; use std::collections::HashMap;
use crate::expressions::lexer::LexerMode; use crate::expressions::lexer::LexerMode;

View File

@@ -5,6 +5,7 @@ use chrono::NaiveDate;
use crate::constants::EXCEL_DATE_BASE; use crate::constants::EXCEL_DATE_BASE;
pub fn from_excel_date(days: i64) -> NaiveDate { pub fn from_excel_date(days: i64) -> NaiveDate {
#[allow(clippy::expect_used)]
let dt = NaiveDate::from_ymd_opt(1900, 1, 1).expect("problem with chrono::NaiveDate"); let dt = NaiveDate::from_ymd_opt(1900, 1, 1).expect("problem with chrono::NaiveDate");
dt + Duration::days(days - 2) dt + Duration::days(days - 2)
} }

View File

@@ -220,7 +220,13 @@ impl Model {
row2 = self row2 = self
.workbook .workbook
.worksheet(left.sheet) .worksheet(left.sheet)
.expect("Sheet expected during evaluation.") .map_err(|_| {
CalcResult::new_error(
Error::ERROR,
*cell,
format!("Invalid worksheet index: '{}'", left.sheet),
)
})?
.dimension() .dimension()
.max_row; .max_row;
} }
@@ -228,7 +234,13 @@ impl Model {
column2 = self column2 = self
.workbook .workbook
.worksheet(left.sheet) .worksheet(left.sheet)
.expect("Sheet expected during evaluation.") .map_err(|_| {
CalcResult::new_error(
Error::ERROR,
*cell,
format!("Invalid worksheet index: '{}'", left.sheet),
)
})?
.dimension() .dimension()
.max_column; .max_column;
} }
@@ -283,7 +295,13 @@ impl Model {
row2 = self row2 = self
.workbook .workbook
.worksheet(left.sheet) .worksheet(left.sheet)
.expect("Sheet expected during evaluation.") .map_err(|_| {
CalcResult::new_error(
Error::ERROR,
*cell,
format!("Invalid worksheet index: '{}'", left.sheet),
)
})?
.dimension() .dimension()
.max_row; .max_row;
} }
@@ -291,7 +309,13 @@ impl Model {
column2 = self column2 = self
.workbook .workbook
.worksheet(left.sheet) .worksheet(left.sheet)
.expect("Sheet expected during evaluation.") .map_err(|_| {
CalcResult::new_error(
Error::ERROR,
*cell,
format!("Invalid worksheet index: '{}'", left.sheet),
)
})?
.dimension() .dimension()
.max_column; .max_column;
} }
@@ -359,7 +383,13 @@ impl Model {
row2 = self row2 = self
.workbook .workbook
.worksheet(left.sheet) .worksheet(left.sheet)
.expect("Sheet expected during evaluation.") .map_err(|_| {
CalcResult::new_error(
Error::ERROR,
*cell,
format!("Invalid worksheet index: '{}'", left.sheet),
)
})?
.dimension() .dimension()
.max_row; .max_row;
} }
@@ -367,7 +397,13 @@ impl Model {
column2 = self column2 = self
.workbook .workbook
.worksheet(left.sheet) .worksheet(left.sheet)
.expect("Sheet expected during evaluation.") .map_err(|_| {
CalcResult::new_error(
Error::ERROR,
*cell,
format!("Invalid worksheet index: '{}'", left.sheet),
)
})?
.dimension() .dimension()
.max_column; .max_column;
} }
@@ -862,20 +898,28 @@ impl Model {
let column1 = left.column; let column1 = left.column;
let mut column2 = right.column; let mut column2 = right.column;
if row1 == 1 && row2 == LAST_ROW { if row1 == 1 && row2 == LAST_ROW {
row2 = self row2 = match self.workbook.worksheet(left.sheet) {
.workbook Ok(s) => s.dimension().max_row,
.worksheet(left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return CalcResult::new_error(
.dimension() Error::ERROR,
.max_row; cell,
format!("Invalid worksheet index: '{}'", left.sheet),
);
}
};
} }
if column1 == 1 && column2 == LAST_COLUMN { if column1 == 1 && column2 == LAST_COLUMN {
column2 = self column2 = match self.workbook.worksheet(left.sheet) {
.workbook Ok(s) => s.dimension().max_column,
.worksheet(left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return CalcResult::new_error(
.dimension() Error::ERROR,
.max_column; cell,
format!("Invalid worksheet index: '{}'", left.sheet),
);
}
};
} }
for row in row1..row2 + 1 { for row in row1..row2 + 1 {
for column in column1..(column2 + 1) { for column in column1..(column2 + 1) {

View File

@@ -128,20 +128,28 @@ impl Model {
let column1 = left.column; let column1 = left.column;
let mut column2 = right.column; let mut column2 = right.column;
if row1 == 1 && row2 == LAST_ROW { if row1 == 1 && row2 == LAST_ROW {
row2 = self row2 = match self.workbook.worksheet(left.sheet) {
.workbook Ok(s) => s.dimension().max_row,
.worksheet(left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return CalcResult::new_error(
.dimension() Error::ERROR,
.max_row; cell,
format!("Invalid worksheet index: '{}'", left.sheet),
);
}
};
} }
if column1 == 1 && column2 == LAST_COLUMN { if column1 == 1 && column2 == LAST_COLUMN {
column2 = self column2 = match self.workbook.worksheet(left.sheet) {
.workbook Ok(s) => s.dimension().max_column,
.worksheet(left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return CalcResult::new_error(
.dimension() Error::ERROR,
.max_column; cell,
format!("Invalid worksheet index: '{}'", left.sheet),
);
}
};
} }
for row in row1..row2 + 1 { for row in row1..row2 + 1 {
for column in column1..(column2 + 1) { for column in column1..(column2 + 1) {
@@ -195,20 +203,28 @@ impl Model {
let column1 = left.column; let column1 = left.column;
let mut column2 = right.column; let mut column2 = right.column;
if row1 == 1 && row2 == LAST_ROW { if row1 == 1 && row2 == LAST_ROW {
row2 = self row2 = match self.workbook.worksheet(left.sheet) {
.workbook Ok(s) => s.dimension().max_row,
.worksheet(left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return CalcResult::new_error(
.dimension() Error::ERROR,
.max_row; cell,
format!("Invalid worksheet index: '{}'", left.sheet),
);
}
};
} }
if column1 == 1 && column2 == LAST_COLUMN { if column1 == 1 && column2 == LAST_COLUMN {
column2 = self column2 = match self.workbook.worksheet(left.sheet) {
.workbook Ok(s) => s.dimension().max_column,
.worksheet(left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return CalcResult::new_error(
.dimension() Error::ERROR,
.max_column; cell,
format!("Invalid worksheet index: '{}'", left.sheet),
);
}
};
} }
for row in row1..row2 + 1 { for row in row1..row2 + 1 {
for column in column1..(column2 + 1) { for column in column1..(column2 + 1) {

View File

@@ -1148,6 +1148,7 @@ impl Model {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
use std::{ use std::{
fs::File, fs::File,
io::{BufRead, BufReader}, io::{BufRead, BufReader},

View File

@@ -381,11 +381,16 @@ impl Model {
let right_row = first_range.right.row; let right_row = first_range.right.row;
let right_column = first_range.right.column; let right_column = first_range.right.column;
let dimension = self let dimension = match self.workbook.worksheet(first_range.left.sheet) {
.workbook Ok(s) => s.dimension(),
.worksheet(first_range.left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return CalcResult::new_error(
.dimension(); Error::ERROR,
cell,
format!("Invalid worksheet index: '{}'", first_range.left.sheet),
)
}
};
let max_row = dimension.max_row; let max_row = dimension.max_row;
let max_column = dimension.max_column; let max_column = dimension.max_column;
@@ -526,20 +531,28 @@ impl Model {
let mut right_column = sum_range.right.column; let mut right_column = sum_range.right.column;
if left_row == 1 && right_row == LAST_ROW { if left_row == 1 && right_row == LAST_ROW {
right_row = self right_row = match self.workbook.worksheet(sum_range.left.sheet) {
.workbook Ok(s) => s.dimension().max_row,
.worksheet(sum_range.left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return Err(CalcResult::new_error(
.dimension() Error::ERROR,
.max_row; cell,
format!("Invalid worksheet index: '{}'", sum_range.left.sheet),
));
}
};
} }
if left_column == 1 && right_column == LAST_COLUMN { if left_column == 1 && right_column == LAST_COLUMN {
right_column = self right_column = match self.workbook.worksheet(sum_range.left.sheet) {
.workbook Ok(s) => s.dimension().max_column,
.worksheet(sum_range.left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return Err(CalcResult::new_error(
.dimension() Error::ERROR,
.max_column; cell,
format!("Invalid worksheet index: '{}'", sum_range.left.sheet),
));
}
};
} }
for row in left_row..right_row + 1 { for row in left_row..right_row + 1 {

View File

@@ -53,8 +53,13 @@ impl Model {
false false
} }
fn cell_hidden_status(&self, sheet_index: u32, row: i32, column: i32) -> CellTableStatus { fn cell_hidden_status(
let worksheet = self.workbook.worksheet(sheet_index).expect(""); &self,
sheet_index: u32,
row: i32,
column: i32,
) -> Result<CellTableStatus, String> {
let worksheet = self.workbook.worksheet(sheet_index)?;
let mut hidden = false; let mut hidden = false;
for row_style in &worksheet.rows { for row_style in &worksheet.rows {
if row_style.r == row { if row_style.r == row {
@@ -63,13 +68,13 @@ impl Model {
} }
} }
if !hidden { if !hidden {
return CellTableStatus::Normal; return Ok(CellTableStatus::Normal);
} }
// The row is hidden we need to know if the table has filters // The row is hidden we need to know if the table has filters
if self.get_table_for_cell(sheet_index, row, column) { if self.get_table_for_cell(sheet_index, row, column) {
CellTableStatus::Filtered Ok(CellTableStatus::Filtered)
} else { } else {
CellTableStatus::Hidden Ok(CellTableStatus::Hidden)
} }
} }
@@ -143,7 +148,11 @@ impl Model {
let column2 = right.column; let column2 = right.column;
for row in row1..=row2 { for row in row1..=row2 {
let cell_status = self.cell_hidden_status(left.sheet, row, column1); let cell_status = self
.cell_hidden_status(left.sheet, row, column1)
.map_err(|message| {
CalcResult::new_error(Error::ERROR, cell, message)
})?;
if cell_status == CellTableStatus::Filtered { if cell_status == CellTableStatus::Filtered {
continue; continue;
} }
@@ -380,7 +389,14 @@ impl Model {
let column2 = right.column; let column2 = right.column;
for row in row1..=row2 { for row in row1..=row2 {
let cell_status = self.cell_hidden_status(left.sheet, row, column1); let cell_status = match self
.cell_hidden_status(left.sheet, row, column1)
{
Ok(s) => s,
Err(message) => {
return CalcResult::new_error(Error::ERROR, cell, message);
}
};
if cell_status == CellTableStatus::Filtered { if cell_status == CellTableStatus::Filtered {
continue; continue;
} }
@@ -449,7 +465,14 @@ impl Model {
let column2 = right.column; let column2 = right.column;
for row in row1..=row2 { for row in row1..=row2 {
let cell_status = self.cell_hidden_status(left.sheet, row, column1); let cell_status = match self
.cell_hidden_status(left.sheet, row, column1)
{
Ok(s) => s,
Err(message) => {
return CalcResult::new_error(Error::ERROR, cell, message);
}
};
if cell_status == CellTableStatus::Filtered { if cell_status == CellTableStatus::Filtered {
continue; continue;
} }

View File

@@ -888,20 +888,28 @@ impl Model {
let column1 = left.column; let column1 = left.column;
let mut column2 = right.column; let mut column2 = right.column;
if row1 == 1 && row2 == LAST_ROW { if row1 == 1 && row2 == LAST_ROW {
row2 = self row2 = match self.workbook.worksheet(left.sheet) {
.workbook Ok(s) => s.dimension().max_row,
.worksheet(left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return CalcResult::new_error(
.dimension() Error::ERROR,
.max_row; cell,
format!("Invalid worksheet index: '{}'", left.sheet),
);
}
};
} }
if column1 == 1 && column2 == LAST_COLUMN { if column1 == 1 && column2 == LAST_COLUMN {
column2 = self column2 = match self.workbook.worksheet(left.sheet) {
.workbook Ok(s) => s.dimension().max_column,
.worksheet(left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return CalcResult::new_error(
.dimension() Error::ERROR,
.max_column; cell,
format!("Invalid worksheet index: '{}'", left.sheet),
);
}
};
} }
for row in row1..row2 + 1 { for row in row1..row2 + 1 {
for column in column1..(column2 + 1) { for column in column1..(column2 + 1) {

View File

@@ -251,20 +251,28 @@ impl Model {
let column1 = left.column; let column1 = left.column;
if row1 == 1 && row2 == LAST_ROW { if row1 == 1 && row2 == LAST_ROW {
row2 = self row2 = match self.workbook.worksheet(left.sheet) {
.workbook Ok(s) => s.dimension().max_row,
.worksheet(left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return CalcResult::new_error(
.dimension() Error::ERROR,
.max_row; cell,
format!("Invalid worksheet index: '{}'", left.sheet),
);
}
};
} }
if column1 == 1 && column2 == LAST_COLUMN { if column1 == 1 && column2 == LAST_COLUMN {
column2 = self column2 = match self.workbook.worksheet(left.sheet) {
.workbook Ok(s) => s.dimension().max_column,
.worksheet(left.sheet) Err(_) => {
.expect("Sheet expected during evaluation.") return CalcResult::new_error(
.dimension() Error::ERROR,
.max_column; cell,
format!("Invalid worksheet index: '{}'", left.sheet),
);
}
};
} }
let left = CellReferenceIndex { let left = CellReferenceIndex {
sheet: left.sheet, sheet: left.sheet,

View File

@@ -31,6 +31,7 @@ pub struct Language {
pub errors: Errors, pub errors: Errors,
} }
#[allow(clippy::expect_used)]
static LANGUAGES: Lazy<HashMap<String, Language>> = Lazy::new(|| { static LANGUAGES: Lazy<HashMap<String, Language>> = Lazy::new(|| {
bitcode::decode(include_bytes!("language.bin")).expect("Failed parsing language file") bitcode::decode(include_bytes!("language.bin")).expect("Failed parsing language file")
}); });

View File

@@ -65,6 +65,7 @@ pub struct DecimalFormats {
pub standard: String, pub standard: String,
} }
#[allow(clippy::expect_used)]
static LOCALES: Lazy<HashMap<String, Locale>> = static LOCALES: Lazy<HashMap<String, Locale>> =
Lazy::new(|| bitcode::decode(include_bytes!("locales.bin")).expect("Failed parsing locale")); Lazy::new(|| bitcode::decode(include_bytes!("locales.bin")).expect("Failed parsing locale"));

View File

@@ -41,6 +41,7 @@ pub use crate::mock_time::get_milliseconds_since_epoch;
/// * Or mocked for tests /// * Or mocked for tests
#[cfg(not(test))] #[cfg(not(test))]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
#[allow(clippy::expect_used)]
pub fn get_milliseconds_since_epoch() -> i64 { pub fn get_milliseconds_since_epoch() -> i64 {
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now() SystemTime::now()
@@ -529,6 +530,7 @@ impl Model {
} }
} }
#[allow(clippy::expect_used)]
fn cell_reference_to_string( fn cell_reference_to_string(
&self, &self,
cell_reference: &CellReferenceIndex, cell_reference: &CellReferenceIndex,
@@ -544,6 +546,7 @@ impl Model {
/// Sets `result` in the cell given by `sheet` sheet index, row and column /// Sets `result` in the cell given by `sheet` sheet index, row and column
/// Note that will panic if the cell does not exist /// Note that will panic if the cell does not exist
/// It will do nothing if the cell does not have a formula /// It will do nothing if the cell does not have a formula
#[allow(clippy::expect_used)]
fn set_cell_value(&mut self, cell_reference: CellReferenceIndex, result: &CalcResult) { fn set_cell_value(&mut self, cell_reference: CellReferenceIndex, result: &CalcResult) {
let CellReferenceIndex { sheet, column, row } = cell_reference; let CellReferenceIndex { sheet, column, row } = cell_reference;
let cell = &self.workbook.worksheets[sheet as usize].sheet_data[&row][&column]; let cell = &self.workbook.worksheets[sheet as usize].sheet_data[&row][&column];
@@ -875,6 +878,7 @@ impl Model {
.map_err(|_| format!("Invalid timezone: {}", workbook.settings.tz))?; .map_err(|_| format!("Invalid timezone: {}", workbook.settings.tz))?;
// FIXME: Add support for display languages // FIXME: Add support for display languages
#[allow(clippy::expect_used)]
let language = get_language("en").expect("").clone(); let language = get_language("en").expect("").clone();
let mut shared_strings = HashMap::new(); let mut shared_strings = HashMap::new();
for (index, s) in workbook.shared_strings.iter().enumerate() { for (index, s) in workbook.shared_strings.iter().enumerate() {
@@ -1986,6 +1990,7 @@ impl Model {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::expect_used)]
use super::CellReferenceIndex as CellReference; use super::CellReferenceIndex as CellReference;
use crate::{test::util::new_empty_model, types::Cell}; use crate::{test::util::new_empty_model, types::Cell};

View File

@@ -392,6 +392,7 @@ impl Model {
let cells = HashMap::new(); let cells = HashMap::new();
// FIXME: Add support for display languages // FIXME: Add support for display languages
#[allow(clippy::expect_used)]
let language = get_language("en").expect("").clone(); let language = get_language("en").expect("").clone();
let mut model = Model { let mut model = Model {

View File

@@ -1,4 +1,5 @@
#![allow(clippy::unwrap_used)] #![allow(clippy::unwrap_used)]
#![allow(clippy::panic)]
use crate::test::util::new_empty_model; use crate::test::util::new_empty_model;
use crate::types::Cell; use crate::types::Cell;

View File

@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
use crate::test::util::new_empty_model; use crate::test::util::new_empty_model;
#[test] #[test]

View File

@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
use crate::{expressions::token, test::util::new_empty_model, types::Cell}; use crate::{expressions::token, test::util::new_empty_model, types::Cell};
#[test] #[test]

View File

@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
use crate::{ use crate::{
constants::{DEFAULT_COLUMN_WIDTH, DEFAULT_ROW_HEIGHT}, constants::{DEFAULT_COLUMN_WIDTH, DEFAULT_ROW_HEIGHT},
test::util::new_empty_model, test::util::new_empty_model,

View File

@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
use crate::{expressions::types::Area, UserModel}; use crate::{expressions::types::Area, UserModel};
#[test] #[test]

View File

@@ -571,7 +571,10 @@ impl UserModel {
break; break;
} }
} }
let data = worksheet.sheet_data.get(&row).unwrap().clone(); let data = match worksheet.sheet_data.get(&row) {
Some(s) => s.clone(),
None => return Err(format!("Row number '{row}' is not valid.")),
};
let old_data = Box::new(RowData { let old_data = Box::new(RowData {
row: row_data, row: row_data,
data, data,
@@ -1369,11 +1372,16 @@ impl UserModel {
); );
text_row.push(text); text_row.push(text);
} }
wtr.write_record(text_row).unwrap(); wtr.write_record(text_row)
.map_err(|e| format!("Error while processing csv: {}", e))?;
data.insert(row, data_row); data.insert(row, data_row);
} }
let csv = String::from_utf8(wtr.into_inner().unwrap()).unwrap(); let csv = String::from_utf8(
wtr.into_inner()
.map_err(|e| format!("Processing error: '{}'", e))?,
)
.map_err(|e| format!("Error converting from utf8: '{}'", e))?;
Ok(Clipboard { Ok(Clipboard {
csv, csv,

View File

@@ -34,6 +34,7 @@ impl ParsedReference {
locale: &Locale, locale: &Locale,
get_sheet_index_by_name: F, get_sheet_index_by_name: F,
) -> Result<ParsedReference, String> { ) -> Result<ParsedReference, String> {
#[allow(clippy::expect_used)]
let language = get_language("en").expect(""); let language = get_language("en").expect("");
let mut lexer = Lexer::new(reference, LexerMode::A1, locale, language); let mut lexer = Lexer::new(reference, LexerMode::A1, locale, language);
@@ -151,6 +152,8 @@ pub(crate) fn is_valid_hex_color(color: &str) -> bool {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::expect_used)]
use super::*; use super::*;
use crate::language::get_language; use crate::language::get_language;
use crate::locale::{get_locale, Locale}; use crate::locale::{get_locale, Locale};

View File

@@ -19,6 +19,3 @@ pyo3 = { version = "0.22.3", features = ["extension-module"] }
[features] [features]
extension-module = ["pyo3/extension-module"] extension-module = ["pyo3/extension-module"]
default = ["extension-module"] default = ["extension-module"]
[tool.maturin]
features = ["pyo3/extension-module"]

View File

@@ -208,6 +208,7 @@ impl PyModel {
.map_err(|e| WorkbookError::new_err(e.to_string())) .map_err(|e| WorkbookError::new_err(e.to_string()))
} }
#[allow(clippy::panic)]
pub fn test_panic(&self) -> PyResult<()> { pub fn test_panic(&self) -> PyResult<()> {
panic!("This function panics for testing panic handling"); panic!("This function panics for testing panic handling");
} }
@@ -240,6 +241,7 @@ pub fn create(name: &str, locale: &str, tz: &str) -> PyResult<PyModel> {
} }
#[pyfunction] #[pyfunction]
#[allow(clippy::panic)]
pub fn test_panic() { pub fn test_panic() {
panic!("This function panics for testing panic handling"); panic!("This function panics for testing panic handling");
} }

View File

@@ -274,15 +274,18 @@ impl Model {
row: i32, row: i32,
column: i32, column: i32,
) -> Result<JsValue, JsError> { ) -> Result<JsValue, JsError> {
self.model let style = self
.model
.get_cell_style(sheet, row, column) .get_cell_style(sheet, row, column)
.map_err(to_js_error) .map_err(to_js_error)?;
.map(|x| serde_wasm_bindgen::to_value(&x).unwrap())
serde_wasm_bindgen::to_value(&style).map_err(|e| to_js_error(e.to_string()))
} }
#[wasm_bindgen(js_name = "onPasteStyles")] #[wasm_bindgen(js_name = "onPasteStyles")]
pub fn on_paste_styles(&mut self, styles: JsValue) -> Result<(), JsError> { pub fn on_paste_styles(&mut self, styles: JsValue) -> Result<(), JsError> {
let styles: &Vec<Vec<Style>> = &serde_wasm_bindgen::from_value(styles).unwrap(); let styles: &Vec<Vec<Style>> =
&serde_wasm_bindgen::from_value(styles).map_err(|e| to_js_error(e.to_string()))?;
self.model.on_paste_styles(styles).map_err(to_js_error) self.model.on_paste_styles(styles).map_err(to_js_error)
} }
@@ -304,7 +307,10 @@ impl Model {
) )
} }
// I don't _think_ serializing to JsValue can't fail
// FIXME: Remove this clippy directive
#[wasm_bindgen(js_name = "getWorksheetsProperties")] #[wasm_bindgen(js_name = "getWorksheetsProperties")]
#[allow(clippy::unwrap_used)]
pub fn get_worksheets_properties(&self) -> JsValue { pub fn get_worksheets_properties(&self) -> JsValue {
serde_wasm_bindgen::to_value(&self.model.get_worksheets_properties()).unwrap() serde_wasm_bindgen::to_value(&self.model.get_worksheets_properties()).unwrap()
} }
@@ -320,7 +326,10 @@ impl Model {
vec![sheet as i32, row, column] vec![sheet as i32, row, column]
} }
// I don't _think_ serializing to JsValue can't fail
// FIXME: Remove this clippy directive
#[wasm_bindgen(js_name = "getSelectedView")] #[wasm_bindgen(js_name = "getSelectedView")]
#[allow(clippy::unwrap_used)]
pub fn get_selected_view(&self) -> JsValue { pub fn get_selected_view(&self) -> JsValue {
serde_wasm_bindgen::to_value(&self.model.get_selected_view()).unwrap() serde_wasm_bindgen::to_value(&self.model.get_selected_view()).unwrap()
} }
@@ -503,8 +512,9 @@ impl Model {
let data = self let data = self
.model .model
.copy_to_clipboard() .copy_to_clipboard()
.map_err(|e| to_js_error(e.to_string())); .map_err(|e| to_js_error(e.to_string()))?;
data.map(|x| serde_wasm_bindgen::to_value(&x).unwrap())
serde_wasm_bindgen::to_value(&data).map_err(|e| to_js_error(e.to_string()))
} }
#[wasm_bindgen(js_name = "pasteFromClipboard")] #[wasm_bindgen(js_name = "pasteFromClipboard")]

View File

@@ -9,17 +9,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
for row in 1..100 { for row in 1..100 {
for column in 1..100 { for column in 1..100 {
let value = row * column; let value = row * column;
model model.set_user_input(0, row, column, format!("{}", value))?;
.set_user_input(0, row, column, format!("{}", value))
.unwrap();
} }
} }
// Adds a new sheet // Adds a new sheet
model.add_sheet("Calculation")?; model.add_sheet("Calculation")?;
// column 100 is CV // column 100 is CV
let last_column = number_to_column(100).unwrap(); let last_column = number_to_column(100).ok_or("Invalid column number")?;
let formula = format!("=SUM(Sheet1!A1:{}100)", last_column); let formula = format!("=SUM(Sheet1!A1:{}100)", last_column);
model.set_user_input(1, 1, 1, formula).unwrap(); model.set_user_input(1, 1, 1, formula)?;
// evaluates // evaluates
model.evaluate(); model.evaluate();

View File

@@ -1,3 +1,6 @@
#![allow(clippy::panic)]
#![allow(clippy::expect_used)]
//! Produces documentation of all the implemented IronCalc functions //! Produces documentation of all the implemented IronCalc functions
//! and saves the result to functions.md //! and saves the result to functions.md
//! //!

View File

@@ -1,3 +1,6 @@
#![allow(clippy::unwrap_used)]
#![allow(clippy::panic)]
//! Tests an Excel xlsx file. //! Tests an Excel xlsx file.
//! Returns a list of differences in json format. //! Returns a list of differences in json format.
//! Saves an IronCalc version //! Saves an IronCalc version

View File

@@ -1,3 +1,6 @@
#![allow(clippy::unwrap_used)]
#![allow(clippy::panic)]
//! Tests an Excel xlsx file. //! Tests an Excel xlsx file.
//! Returns a list of differences in json format. //! Returns a list of differences in json format.
//! Saves an IronCalc version //! Saves an IronCalc version

View File

@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
use std::path::Path; use std::path::Path;
use ironcalc_base::cell::CellValue; use ironcalc_base::cell::CellValue;

View File

@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
mod _rels; mod _rels;
mod doc_props; mod doc_props;
mod escape; mod escape;

View File

@@ -1,3 +1,4 @@
#![allow(clippy::unwrap_used)]
//! <sheet name="Sheet1" sheetId="1" r:id="rId1"/> //! <sheet name="Sheet1" sheetId="1" r:id="rId1"/>
//! A workbook is composed of workbook-level properties and a collection of 1 or more sheets. //! A workbook is composed of workbook-level properties and a collection of 1 or more sheets.

View File

@@ -1,3 +1,6 @@
#![allow(clippy::unwrap_used)]
#![allow(clippy::panic)]
//! # A note on shared formulas //! # A note on shared formulas
//! Although both Excel and IronCalc uses shared formulas they are used in a slightly different way that cannot be mapped 1-1 //! Although both Excel and IronCalc uses shared formulas they are used in a slightly different way that cannot be mapped 1-1
//! In IronCalc _all_ formulas are shared and there is a list of shared formulas much like there is a list of shared strings. //! In IronCalc _all_ formulas are shared and there is a list of shared formulas much like there is a list of shared strings.

View File

@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
use core::cmp::max; use core::cmp::max;
use core::cmp::min; use core::cmp::min;

View File

@@ -38,6 +38,7 @@ fn read_shared_strings_from_string(text: &str) -> Result<Vec<String>, XlsxError>
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)]
use super::*; use super::*;
#[test] #[test]

View File

@@ -41,29 +41,35 @@ pub(crate) fn load_table<R: Read + std::io::Seek>(
// They also need to be different from any defined name // They also need to be different from any defined name
let name = table let name = table
.attribute("name") .attribute("name")
.expect("Missing table name") .ok_or_else(|| XlsxError::Xml("Corrupt XML structure: missing table name".to_string()))?
.to_string(); .to_string();
let display_name = table let display_name = table
.attribute("name") .attribute("name")
.expect("Missing table display name") .ok_or_else(|| {
XlsxError::Xml("Corrupt XML structure: missing table display name".to_string())
})?
.to_string(); .to_string();
// Range of the table, including the totals if any and headers. // Range of the table, including the totals if any and headers.
let reference = table let reference = table
.attribute("ref") .attribute("ref")
.expect("Missing table ref") .ok_or_else(|| XlsxError::Xml("Corrupt XML structure: missing table ref".to_string()))?
.to_string(); .to_string();
// Either 0 or 1, indicates if the table has a formula for totals at the bottom of the table // Either 0 or 1, indicates if the table has a formula for totals at the bottom of the table
let totals_row_count = match table.attribute("totalsRowCount") { let totals_row_count = match table.attribute("totalsRowCount") {
Some(s) => s.parse::<u32>().expect("Invalid totalsRowCount"), Some(s) => s.parse::<u32>().map_err(|_| {
XlsxError::Xml("Corrupt XML structure: Invalid totalsRowCount".to_string())
})?,
None => 0, None => 0,
}; };
// Either 0 or 1, indicates if the table has headers at the top of the table // Either 0 or 1, indicates if the table has headers at the top of the table
let header_row_count = match table.attribute("headerRowCount") { let header_row_count = match table.attribute("headerRowCount") {
Some(s) => s.parse::<u32>().expect("Invalid headerRowCount"), Some(s) => s.parse::<u32>().map_err(|_| {
XlsxError::Xml("Corrupt XML structure: Invalid headerRowCount".to_string())
})?,
None => 1, None => 1,
}; };
@@ -125,9 +131,15 @@ pub(crate) fn load_table<R: Read + std::io::Seek>(
.collect::<Vec<Node>>(); .collect::<Vec<Node>>();
let mut columns = Vec::new(); let mut columns = Vec::new();
for table_column in table_column { for table_column in table_column {
let column_name = table_column.attribute("name").expect("Missing column name"); let column_name = table_column.attribute("name").ok_or_else(|| {
let id = table_column.attribute("id").expect("Missing column id"); XlsxError::Xml("Corrupt XML structure: missing column name".to_string())
let id = id.parse::<u32>().expect("Invalid id"); })?;
let id = table_column.attribute("id").ok_or_else(|| {
XlsxError::Xml("Corrupt XML structure: missing column id".to_string())
})?;
let id = id
.parse::<u32>()
.map_err(|_| XlsxError::Xml("Corrupt XML structure: invalid id".to_string()))?;
// style index of the header row of the table // style index of the header row of the table
let header_row_dxf_id = if let Some(index_str) = table_column.attribute("headerRowDxfId") { let header_row_dxf_id = if let Some(index_str) = table_column.attribute("headerRowDxfId") {

View File

@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
use colors::{get_indexed_color, get_themed_color}; use colors::{get_indexed_color, get_themed_color};
use roxmltree::{ExpandedName, Node}; use roxmltree::{ExpandedName, Node};

View File

@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
use std::{collections::HashMap, io::Read, num::ParseIntError}; use std::{collections::HashMap, io::Read, num::ParseIntError};
use ironcalc_base::{ use ironcalc_base::{
@@ -1037,7 +1039,10 @@ pub(super) fn load_sheets<R: Read + std::io::Seek>(
name: sheet_name.to_string(), name: sheet_name.to_string(),
id: sheet.sheet_id, id: sheet.sheet_id,
state: state.clone(), state: state.clone(),
comments: comments.get(rel_id).expect("").to_vec(), comments: comments
.get(rel_id)
.ok_or_else(|| XlsxError::Xml("Corrupt XML structure".to_string()))?
.to_vec(),
}; };
let (s, is_selected) = let (s, is_selected) =
load_sheet(archive, &path, settings, worksheets, tables, shared_strings)?; load_sheet(archive, &path, settings, worksheets, tables, shared_strings)?;

View File

@@ -1,3 +1,6 @@
#![allow(clippy::unwrap_used)]
#![allow(clippy::panic)]
use std::io::Read; use std::io::Read;
use std::{env, fs, io}; use std::{env, fs, io};
use uuid::Uuid; use uuid::Uuid;