UPDATE: Update README to include a minimal ROADMAD

This commit is contained in:
Nicolás Hatcher
2023-12-01 08:53:12 +01:00
parent c5b8efd83d
commit c63acd72d0
8 changed files with 229 additions and 11 deletions

View File

@@ -127,7 +127,6 @@ impl Cell {
Cell::BooleanCell { .. } => CellType::LogicalValue,
Cell::NumberCell { .. } => CellType::Number,
Cell::ErrorCell { .. } => CellType::ErrorValue,
// TODO: An empty string should likely be considered a Number (like an empty cell).
Cell::SharedString { .. } => CellType::Text,
Cell::CellFormula { .. } => CellType::Number,
Cell::CellFormulaBoolean { .. } => CellType::LogicalValue,

View File

@@ -7,7 +7,7 @@ use std::vec::Vec;
use crate::{
calc_result::{CalcResult, CellReference, Range},
cell::CellValue,
constants,
constants::{self, LAST_COLUMN, LAST_ROW},
expressions::token::{Error, OpCompare, OpProduct, OpSum, OpUnary},
expressions::{
parser::move_formula::{move_formula, MoveContext},
@@ -1207,7 +1207,6 @@ impl Model {
}
}
// FIXME: Can't put it in Workbook, because language is outside of workbook, sic!
/// Gets the Excel Value (Bool, Number, String) of a cell
pub fn get_cell_value_by_ref(&self, cell_ref: &str) -> Result<CellValue, String> {
let cell_reference = match self.parse_reference(cell_ref) {
@@ -1221,7 +1220,6 @@ impl Model {
self.get_cell_value_by_index(sheet_index, row, column)
}
// FIXME: Can't put it in Workbook, because language is outside of workbook, sic!
pub fn get_cell_value_by_index(
&self,
sheet_index: u32,
@@ -1238,7 +1236,6 @@ impl Model {
Ok(cell_value)
}
// FIXME: Can't put it in Workbook, because locale and language are outside of workbook, sic!
pub fn formatted_cell_value(
&self,
sheet_index: u32,
@@ -1259,6 +1256,28 @@ impl Model {
Ok(formatted_value)
}
/// Returns a string with the cell content. If there is a formula returns the formula
/// If the cell is empty returns the empty string
/// Raises an error if there is no worksheet
pub fn get_cell_content(&self, sheet: u32, row: i32, column: i32) -> Result<String, String> {
let worksheet = self.workbook.worksheet(sheet)?;
let cell = match worksheet.cell(row, column) {
Some(c) => c,
None => return Ok("".to_string()),
};
match cell.get_formula() {
Some(formula_index) => {
let formula = &self.parsed_formulas[sheet as usize][formula_index as usize];
let cell_ref = CellReferenceRC {
sheet: worksheet.get_name(),
row,
column,
};
Ok(format!("={}", to_string(formula, &cell_ref)))
}
None => Ok(cell.get_text(&self.workbook.shared_strings, &self.language)),
}
}
/// Returns a list of all cells
pub fn get_all_cells(&self) -> Vec<CellIndex> {
let mut cells = Vec::new();
@@ -1316,7 +1335,6 @@ impl Model {
Ok(())
}
// FIXME: expect
pub fn get_cell_style_index(&self, sheet: u32, row: i32, column: i32) -> i32 {
// First check the cell, then row, the column
let cell = self
@@ -1414,6 +1432,50 @@ impl Model {
};
Ok(())
}
pub fn get_frozen_rows(&self, sheet: u32) -> Result<i32, String> {
if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
Ok(worksheet.frozen_rows)
} else {
Err("Invalid sheet".to_string())
}
}
pub fn get_frozen_columns(&self, sheet: u32) -> Result<i32, String> {
if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
Ok(worksheet.frozen_columns)
} else {
Err("Invalid sheet".to_string())
}
}
pub fn set_frozen_rows(&mut self, sheet: u32, frozen_rows: i32) -> Result<(), String> {
if let Some(worksheet) = self.workbook.worksheets.get_mut(sheet as usize) {
if frozen_rows < 0 {
return Err("Frozen rows cannot be negative".to_string());
} else if frozen_rows >= LAST_ROW {
return Err("Too many rows".to_string());
}
worksheet.frozen_rows = frozen_rows;
Ok(())
} else {
Err("Invalid sheet".to_string())
}
}
pub fn set_frozen_columns(&mut self, sheet: u32, frozen_columns: i32) -> Result<(), String> {
if let Some(worksheet) = self.workbook.worksheets.get_mut(sheet as usize) {
if frozen_columns < 0 {
return Err("Frozen columns cannot be negative".to_string());
} else if frozen_columns >= LAST_COLUMN {
return Err("Too many columns".to_string());
}
worksheet.frozen_columns = frozen_columns;
Ok(())
} else {
Err("Invalid sheet".to_string())
}
}
}
#[cfg(test)]

View File

@@ -47,5 +47,6 @@ mod test_number_format;
mod test_escape_quotes;
mod test_fn_type;
mod test_get_cell_content;
mod test_percentage;
mod test_today;

View File

@@ -0,0 +1,71 @@
#![allow(clippy::unwrap_used)]
use crate::expressions::utils::{LAST_COLUMN, LAST_ROW};
use crate::test::util::new_empty_model;
#[test]
fn test_empty_model() {
let mut model = new_empty_model();
assert_eq!(model.get_frozen_rows(0), Ok(0));
assert_eq!(model.get_frozen_columns(0), Ok(0));
let e = model.set_frozen_rows(0, 3);
assert!(e.is_ok());
assert_eq!(model.get_frozen_rows(0), Ok(3));
assert_eq!(model.get_frozen_columns(0), Ok(0));
let e = model.set_frozen_columns(0, 53);
assert!(e.is_ok());
assert_eq!(model.get_frozen_rows(0), Ok(3));
assert_eq!(model.get_frozen_columns(0), Ok(53));
// Set them back to zero
let e = model.set_frozen_rows(0, 0);
assert!(e.is_ok());
let e = model.set_frozen_columns(0, 0);
assert!(e.is_ok());
assert_eq!(model.get_frozen_rows(0), Ok(0));
assert_eq!(model.get_frozen_columns(0), Ok(0));
}
#[test]
fn test_invalid_sheet() {
let mut model = new_empty_model();
assert_eq!(model.get_frozen_rows(1), Err("Invalid sheet".to_string()));
assert_eq!(
model.get_frozen_columns(3),
Err("Invalid sheet".to_string())
);
assert_eq!(
model.set_frozen_rows(3, 3),
Err("Invalid sheet".to_string())
);
assert_eq!(
model.set_frozen_columns(3, 5),
Err("Invalid sheet".to_string())
);
}
#[test]
fn test_invalid_rows_columns() {
let mut model = new_empty_model();
assert_eq!(
model.set_frozen_rows(0, -3),
Err("Frozen rows cannot be negative".to_string())
);
assert_eq!(
model.set_frozen_columns(0, -5),
Err("Frozen columns cannot be negative".to_string())
);
assert_eq!(
model.set_frozen_rows(0, LAST_ROW),
Err("Too many rows".to_string())
);
assert_eq!(
model.set_frozen_columns(0, LAST_COLUMN),
Err("Too many columns".to_string())
);
}

View File

@@ -0,0 +1,18 @@
#![allow(clippy::unwrap_used)]
use crate::test::util::new_empty_model;
#[test]
fn test_formulas() {
let mut model = new_empty_model();
model.set_user_input(0, 1, 1, "$100.348".to_string());
model.set_user_input(0, 1, 2, "=ISNUMBER(A1)".to_string());
model.evaluate();
assert_eq!(model.get_cell_content(0, 1, 1).unwrap(), "100.348");
assert_eq!(model.get_cell_content(0, 1, 2).unwrap(), "=ISNUMBER(A1)");
assert_eq!(model.get_cell_content(0, 5, 5).unwrap(), "");
assert!(model.get_cell_content(1, 1, 2).is_err());
}

View File

@@ -651,3 +651,13 @@ pub struct Border {
#[serde(skip_serializing_if = "Option::is_none")]
pub diagonal: Option<BorderItem>,
}
/// Information need to show a sheet tab in the UI
/// The color is serialized only if it is not Color::None
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct SheetInfo {
pub name: String,
pub state: String,
pub sheet_id: u32,
pub color: Option<String>,
}

View File

@@ -27,4 +27,16 @@ impl Workbook {
.get_mut(worksheet_index as usize)
.ok_or_else(|| "Invalid sheet index".to_string())
}
pub fn get_worksheets_info(&self) -> Vec<SheetInfo> {
self.worksheets
.iter()
.map(|worksheet| SheetInfo {
name: worksheet.get_name(),
state: worksheet.state.to_string(),
color: worksheet.color.clone(),
sheet_id: worksheet.sheet_id,
})
.collect()
}
}