UPDATE: Adds 'user model' API (#27)
* bump version for documentation * Fixes wrong doc comment * renames old APIs to be consistent
This commit is contained in:
committed by
GitHub
parent
e9fc41541b
commit
d445553d85
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -204,7 +204,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ironcalc_base"
|
name = "ironcalc_base"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-tz",
|
"chrono-tz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ironcalc_base"
|
name = "ironcalc_base"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
authors = ["Nicolás Hatcher <nicolas@theuniverse.today>"]
|
authors = ["Nicolás Hatcher <nicolas@theuniverse.today>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
homepage = "https://www.ironcalc.com"
|
homepage = "https://www.ironcalc.com"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use ironcalc_base::{model::Model, types::CellType};
|
use ironcalc_base::{types::CellType, Model};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut model = Model::new_empty("formulas-and-errors", "en", "UTC")?;
|
let mut model = Model::new_empty("formulas-and-errors", "en", "UTC")?;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use ironcalc_base::{cell::CellValue, model::Model};
|
use ironcalc_base::{cell::CellValue, Model};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut model = Model::new_empty("hello-world", "en", "UTC")?;
|
let mut model = Model::new_empty("hello-world", "en", "UTC")?;
|
||||||
|
|||||||
@@ -77,13 +77,13 @@ impl Model {
|
|||||||
let style = source_cell.get_style();
|
let style = source_cell.get_style();
|
||||||
// FIXME: we need some user_input getter instead of get_text
|
// FIXME: we need some user_input getter instead of get_text
|
||||||
let formula_or_value = self
|
let formula_or_value = self
|
||||||
.cell_formula(sheet, source_row, source_column)?
|
.get_cell_formula(sheet, source_row, source_column)?
|
||||||
.unwrap_or_else(|| source_cell.get_text(&self.workbook.shared_strings, &self.language));
|
.unwrap_or_else(|| source_cell.get_text(&self.workbook.shared_strings, &self.language));
|
||||||
self.set_user_input(sheet, target_row, target_column, formula_or_value);
|
self.set_user_input(sheet, target_row, target_column, formula_or_value);
|
||||||
self.workbook
|
self.workbook
|
||||||
.worksheet_mut(sheet)?
|
.worksheet_mut(sheet)?
|
||||||
.set_cell_style(target_row, target_column, style);
|
.set_cell_style(target_row, target_column, style);
|
||||||
self.delete_cell(sheet, source_row, source_column)?;
|
self.cell_clear_all(sheet, source_row, source_column)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +157,11 @@ impl Model {
|
|||||||
return Err("Please use insert columns instead".to_string());
|
return Err("Please use insert columns instead".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// first column being deleted
|
||||||
|
let column_start = column;
|
||||||
|
// last column being deleted
|
||||||
|
let column_end = column + column_count - 1;
|
||||||
|
|
||||||
// Move cells
|
// Move cells
|
||||||
let worksheet = &self.workbook.worksheet(sheet)?;
|
let worksheet = &self.workbook.worksheet(sheet)?;
|
||||||
let mut all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
|
let mut all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
|
||||||
@@ -166,11 +171,11 @@ impl Model {
|
|||||||
for r in all_rows {
|
for r in all_rows {
|
||||||
let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
|
let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
|
||||||
for col in columns {
|
for col in columns {
|
||||||
if col >= column {
|
if col >= column_start {
|
||||||
if col >= column + column_count {
|
if col > column_end {
|
||||||
self.move_cell(sheet, r, col, r, col - column_count)?;
|
self.move_cell(sheet, r, col, r, col - column_count)?;
|
||||||
} else {
|
} else {
|
||||||
self.delete_cell(sheet, r, col)?;
|
self.cell_clear_all(sheet, r, col)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,6 +189,64 @@ impl Model {
|
|||||||
delta: -column_count,
|
delta: -column_count,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
let worksheet = &mut self.workbook.worksheet_mut(sheet)?;
|
||||||
|
|
||||||
|
// deletes all the column styles
|
||||||
|
let mut new_columns = Vec::new();
|
||||||
|
for col in worksheet.cols.iter_mut() {
|
||||||
|
// range under study
|
||||||
|
let min = col.min;
|
||||||
|
let max = col.max;
|
||||||
|
// In the diagram:
|
||||||
|
// |xxxxx| range we are studying [min, max]
|
||||||
|
// |*****| range we are deleting [column_start, column_end]
|
||||||
|
// we are going to split it in three big cases:
|
||||||
|
// ----------------|xxxxxxxx|-----------------
|
||||||
|
// -----|*****|------------------------------- Case A
|
||||||
|
// -------|**********|------------------------ Case B
|
||||||
|
// -------------|**************|-------------- Case C
|
||||||
|
// ------------------|****|------------------- Case D
|
||||||
|
// ---------------------|**********|---------- Case E
|
||||||
|
// -----------------------------|*****|------- Case F
|
||||||
|
if column_start < min {
|
||||||
|
if column_end < min {
|
||||||
|
// Case A
|
||||||
|
// We displace all columns
|
||||||
|
let mut new_column = col.clone();
|
||||||
|
new_column.min = min - column_count;
|
||||||
|
new_column.max = max - column_count;
|
||||||
|
new_columns.push(new_column);
|
||||||
|
} else if column_end < max {
|
||||||
|
// Case B
|
||||||
|
// We displace the end
|
||||||
|
let mut new_column = col.clone();
|
||||||
|
new_column.min = column_start;
|
||||||
|
new_column.max = max - column_count;
|
||||||
|
new_columns.push(new_column);
|
||||||
|
} else {
|
||||||
|
// Case C
|
||||||
|
// skip this, we are deleting the whole range
|
||||||
|
}
|
||||||
|
} else if column_start <= max {
|
||||||
|
if column_end <= max {
|
||||||
|
// Case D
|
||||||
|
// We displace the end
|
||||||
|
let mut new_column = col.clone();
|
||||||
|
new_column.max = max - column_count;
|
||||||
|
new_columns.push(new_column);
|
||||||
|
} else {
|
||||||
|
// Case E
|
||||||
|
let mut new_column = col.clone();
|
||||||
|
new_column.max = column_start - 1;
|
||||||
|
new_columns.push(new_column);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Case F
|
||||||
|
// No action required
|
||||||
|
new_columns.push(col.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
worksheet.cols = new_columns;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -283,7 +346,7 @@ impl Model {
|
|||||||
// remove all cells in row
|
// remove all cells in row
|
||||||
// FIXME: We could just remove the entire row in one go
|
// FIXME: We could just remove the entire row in one go
|
||||||
for column in columns {
|
for column in columns {
|
||||||
self.delete_cell(sheet, r, column)?;
|
self.cell_clear_all(sheet, r, column)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,8 @@ impl Model {
|
|||||||
message: "argument must be a reference to a single cell".to_string(),
|
message: "argument must be a reference to a single cell".to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let is_formula = if let Ok(f) = self.cell_formula(left.sheet, left.row, left.column) {
|
let is_formula = if let Ok(f) = self.get_cell_formula(left.sheet, left.row, left.column)
|
||||||
|
{
|
||||||
f.is_some()
|
f.is_some()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! [dependencies]
|
//! [dependencies]
|
||||||
//! ironcalc_base = { git = "https://github.com/ironcalc/IronCalc", version = "0.1"}
|
//! ironcalc_base = { git = "https://github.com/ironcalc/IronCalc" }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! <small> until version 0.5.0 you should use the git dependencies as stated </small>
|
//! <small> until version 0.5.0 you should use the git dependencies as stated </small>
|
||||||
@@ -31,23 +31,21 @@ pub mod expressions;
|
|||||||
pub mod formatter;
|
pub mod formatter;
|
||||||
pub mod language;
|
pub mod language;
|
||||||
pub mod locale;
|
pub mod locale;
|
||||||
pub mod model;
|
|
||||||
pub mod new_empty;
|
pub mod new_empty;
|
||||||
pub mod number_format;
|
pub mod number_format;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod worksheet;
|
pub mod worksheet;
|
||||||
|
|
||||||
mod functions;
|
|
||||||
|
|
||||||
mod actions;
|
mod actions;
|
||||||
mod cast;
|
mod cast;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod styles;
|
|
||||||
|
|
||||||
mod diffs;
|
mod diffs;
|
||||||
|
mod functions;
|
||||||
mod implicit_intersection;
|
mod implicit_intersection;
|
||||||
|
mod model;
|
||||||
|
mod styles;
|
||||||
mod units;
|
mod units;
|
||||||
|
mod user_model;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod workbook;
|
mod workbook;
|
||||||
|
|
||||||
@@ -56,3 +54,7 @@ mod test;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod mock_time;
|
pub mod mock_time;
|
||||||
|
|
||||||
|
pub use model::get_milliseconds_since_epoch;
|
||||||
|
pub use model::Model;
|
||||||
|
pub use user_model::UserModel;
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
//! # Model
|
|
||||||
//!
|
|
||||||
//! Note that sheets are 0-indexed and rows and columns are 1-indexed.
|
|
||||||
//!
|
|
||||||
//! IronCalc is row first. A cell is referenced by (`sheet`, `row`, `column`)
|
|
||||||
//!
|
|
||||||
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -47,7 +40,11 @@ use chrono_tz::Tz;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use crate::mock_time::get_milliseconds_since_epoch;
|
pub use crate::mock_time::get_milliseconds_since_epoch;
|
||||||
|
|
||||||
/// wasm implementation for time
|
/// Number of milliseconds since January 1, 1970
|
||||||
|
/// Used by time and date functions. It takes the value from the environment:
|
||||||
|
/// * The Operative System
|
||||||
|
/// * The JavaScript environment
|
||||||
|
/// * Or mocked for tests
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub fn get_milliseconds_since_epoch() -> i64 {
|
pub fn get_milliseconds_since_epoch() -> i64 {
|
||||||
@@ -67,7 +64,7 @@ pub fn get_milliseconds_since_epoch() -> i64 {
|
|||||||
|
|
||||||
/// A cell might be evaluated or being evaluated
|
/// A cell might be evaluated or being evaluated
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum CellState {
|
pub(crate) enum CellState {
|
||||||
/// The cell has already been evaluated
|
/// The cell has already been evaluated
|
||||||
Evaluated,
|
Evaluated,
|
||||||
/// The cell is being evaluated
|
/// The cell is being evaluated
|
||||||
@@ -75,7 +72,7 @@ pub enum CellState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A parsed formula for a defined name
|
/// A parsed formula for a defined name
|
||||||
pub enum ParsedDefinedName {
|
pub(crate) enum ParsedDefinedName {
|
||||||
/// CellReference (`=C4`)
|
/// CellReference (`=C4`)
|
||||||
CellReference(CellReferenceIndex),
|
CellReference(CellReferenceIndex),
|
||||||
/// A Range (`=C4:D6`)
|
/// A Range (`=C4:D6`)
|
||||||
@@ -105,19 +102,19 @@ pub struct Model {
|
|||||||
/// A list of parsed formulas
|
/// A list of parsed formulas
|
||||||
pub parsed_formulas: Vec<Vec<Node>>,
|
pub parsed_formulas: Vec<Vec<Node>>,
|
||||||
/// A list of parsed defined names
|
/// A list of parsed defined names
|
||||||
pub parsed_defined_names: HashMap<(Option<u32>, String), ParsedDefinedName>,
|
pub(crate) parsed_defined_names: HashMap<(Option<u32>, String), ParsedDefinedName>,
|
||||||
/// An optimization to lookup strings faster
|
/// An optimization to lookup strings faster
|
||||||
pub shared_strings: HashMap<String, usize>,
|
pub(crate) shared_strings: HashMap<String, usize>,
|
||||||
/// An instance of the parser
|
/// An instance of the parser
|
||||||
pub parser: Parser,
|
pub(crate) parser: Parser,
|
||||||
/// The list of cells with formulas that are evaluated of being evaluated
|
/// The list of cells with formulas that are evaluated of being evaluated
|
||||||
pub cells: HashMap<(u32, i32, i32), CellState>,
|
pub(crate) cells: HashMap<(u32, i32, i32), CellState>,
|
||||||
/// The locale of the model
|
/// The locale of the model
|
||||||
pub locale: Locale,
|
pub(crate) locale: Locale,
|
||||||
/// Tha language used
|
/// Tha language used
|
||||||
pub language: Language,
|
pub(crate) language: Language,
|
||||||
/// The timezone used to evaluate the model
|
/// The timezone used to evaluate the model
|
||||||
pub tz: Tz,
|
pub(crate) tz: Tz,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Maybe this should be the same as CellReference
|
// FIXME: Maybe this should be the same as CellReference
|
||||||
@@ -659,7 +656,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
/// assert_eq!(model.workbook.worksheet(0)?.color, None);
|
/// assert_eq!(model.workbook.worksheet(0)?.color, None);
|
||||||
@@ -726,7 +723,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
/// assert_eq!(model.is_empty_cell(0, 1, 1)?, true);
|
/// assert_eq!(model.is_empty_cell(0, 1, 1)?, true);
|
||||||
@@ -803,7 +800,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # use ironcalc_base::cell::CellValue;
|
/// # use ironcalc_base::cell::CellValue;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
@@ -827,7 +824,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # use ironcalc_base::cell::CellValue;
|
/// # use ironcalc_base::cell::CellValue;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
@@ -896,7 +893,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # use ironcalc_base::expressions::types::CellReferenceIndex;
|
/// # use ironcalc_base::expressions::types::CellReferenceIndex;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
@@ -965,7 +962,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # use ironcalc_base::expressions::types::{Area, CellReferenceIndex};
|
/// # use ironcalc_base::expressions::types::{Area, CellReferenceIndex};
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
@@ -1036,7 +1033,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
/// let (sheet, row, column) = (0, 1, 1);
|
/// let (sheet, row, column) = (0, 1, 1);
|
||||||
@@ -1083,7 +1080,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # use ironcalc_base::expressions::types::CellReferenceIndex;
|
/// # use ironcalc_base::expressions::types::CellReferenceIndex;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
@@ -1138,13 +1135,13 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
/// let (sheet, row, column) = (0, 1, 1);
|
/// let (sheet, row, column) = (0, 1, 1);
|
||||||
/// model.set_user_input(sheet, row, column, "=SIN(B1*C3)+1".to_string());
|
/// model.set_user_input(sheet, row, column, "=SIN(B1*C3)+1".to_string());
|
||||||
/// model.evaluate();
|
/// model.evaluate();
|
||||||
/// let result = model.cell_formula(sheet, row, column)?;
|
/// let result = model.get_cell_formula(sheet, row, column)?;
|
||||||
/// assert_eq!(result, Some("=SIN(B1*C3)+1".to_string()));
|
/// assert_eq!(result, Some("=SIN(B1*C3)+1".to_string()));
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
@@ -1152,24 +1149,33 @@ impl Model {
|
|||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
/// * [Model::get_cell_content()]
|
/// * [Model::get_cell_content()]
|
||||||
pub fn cell_formula(
|
pub fn get_cell_formula(
|
||||||
&self,
|
&self,
|
||||||
sheet: u32,
|
sheet: u32,
|
||||||
row: i32,
|
row: i32,
|
||||||
column: i32,
|
column: i32,
|
||||||
) -> Result<Option<String>, String> {
|
) -> Result<Option<String>, String> {
|
||||||
let worksheet = self.workbook.worksheet(sheet)?;
|
let worksheet = self.workbook.worksheet(sheet)?;
|
||||||
Ok(worksheet.cell(row, column).and_then(|cell| {
|
match worksheet.cell(row, column) {
|
||||||
cell.get_formula().map(|formula_index| {
|
Some(cell) => match cell.get_formula() {
|
||||||
let formula = &self.parsed_formulas[sheet as usize][formula_index as usize];
|
Some(formula_index) => {
|
||||||
|
let formula = &self
|
||||||
|
.parsed_formulas
|
||||||
|
.get(sheet as usize)
|
||||||
|
.ok_or("missing sheet")?
|
||||||
|
.get(formula_index as usize)
|
||||||
|
.ok_or("missing formula")?;
|
||||||
let cell_ref = CellReferenceRC {
|
let cell_ref = CellReferenceRC {
|
||||||
sheet: worksheet.get_name(),
|
sheet: worksheet.get_name(),
|
||||||
row,
|
row,
|
||||||
column,
|
column,
|
||||||
};
|
};
|
||||||
format!("={}", to_string(formula, &cell_ref))
|
Ok(Some(format!("={}", to_string(formula, &cell_ref))))
|
||||||
})
|
}
|
||||||
}))
|
None => Ok(None),
|
||||||
|
},
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the value of a cell with some text
|
/// Updates the value of a cell with some text
|
||||||
@@ -1178,7 +1184,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
/// let (sheet, row, column) = (0, 1, 1);
|
/// let (sheet, row, column) = (0, 1, 1);
|
||||||
@@ -1221,7 +1227,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
/// let (sheet, row, column) = (0, 1, 1);
|
/// let (sheet, row, column) = (0, 1, 1);
|
||||||
@@ -1258,7 +1264,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
/// let (sheet, row, column) = (0, 1, 1);
|
/// let (sheet, row, column) = (0, 1, 1);
|
||||||
@@ -1296,7 +1302,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
/// let (sheet, row, column) = (0, 1, 1);
|
/// let (sheet, row, column) = (0, 1, 1);
|
||||||
@@ -1348,7 +1354,7 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # use ironcalc_base::cell::CellValue;
|
/// # use ironcalc_base::cell::CellValue;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
@@ -1358,7 +1364,7 @@ impl Model {
|
|||||||
/// model.set_user_input(0, 1, 2, "=SUM(A:A)".to_string());
|
/// model.set_user_input(0, 1, 2, "=SUM(A:A)".to_string());
|
||||||
/// model.evaluate();
|
/// model.evaluate();
|
||||||
/// assert_eq!(model.get_cell_value_by_index(0, 1, 2), Ok(CellValue::Number(215.0)));
|
/// assert_eq!(model.get_cell_value_by_index(0, 1, 2), Ok(CellValue::Number(215.0)));
|
||||||
/// assert_eq!(model.formatted_cell_value(0, 1, 2), Ok("215$".to_string()));
|
/// assert_eq!(model.get_formatted_cell_value(0, 1, 2), Ok("215$".to_string()));
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
@@ -1529,7 +1535,7 @@ impl Model {
|
|||||||
/// Returns the cell value for (`sheet`, `row`, `column`)
|
/// Returns the cell value for (`sheet`, `row`, `column`)
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
/// * [Model::formatted_cell_value()]
|
/// * [Model::get_formatted_cell_value()]
|
||||||
pub fn get_cell_value_by_index(
|
pub fn get_cell_value_by_index(
|
||||||
&self,
|
&self,
|
||||||
sheet_index: u32,
|
sheet_index: u32,
|
||||||
@@ -1555,36 +1561,35 @@ impl Model {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ironcalc_base::model::Model;
|
/// # use ironcalc_base::Model;
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
/// let (sheet, row, column) = (0, 1, 1);
|
/// let (sheet, row, column) = (0, 1, 1);
|
||||||
/// model.set_user_input(sheet, row, column, "=1/3".to_string());
|
/// model.set_user_input(sheet, row, column, "=1/3".to_string());
|
||||||
/// model.evaluate();
|
/// model.evaluate();
|
||||||
/// let result = model.formatted_cell_value(sheet, row, column)?;
|
/// let result = model.get_formatted_cell_value(sheet, row, column)?;
|
||||||
/// assert_eq!(result, "0.333333333".to_string());
|
/// assert_eq!(result, "0.333333333".to_string());
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn formatted_cell_value(
|
pub fn get_formatted_cell_value(
|
||||||
&self,
|
&self,
|
||||||
sheet_index: u32,
|
sheet_index: u32,
|
||||||
row: i32,
|
row: i32,
|
||||||
column: i32,
|
column: i32,
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
|
match self.workbook.worksheet(sheet_index)?.cell(row, column) {
|
||||||
|
Some(cell) => {
|
||||||
let format = self.get_style_for_cell(sheet_index, row, column).num_fmt;
|
let format = self.get_style_for_cell(sheet_index, row, column).num_fmt;
|
||||||
let cell = self
|
|
||||||
.workbook
|
|
||||||
.worksheet(sheet_index)?
|
|
||||||
.cell(row, column)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default();
|
|
||||||
let formatted_value =
|
let formatted_value =
|
||||||
cell.formatted_value(&self.workbook.shared_strings, &self.language, |value| {
|
cell.formatted_value(&self.workbook.shared_strings, &self.language, |value| {
|
||||||
format_number(value, &format, &self.locale).text
|
format_number(value, &format, &self.locale).text
|
||||||
});
|
});
|
||||||
Ok(formatted_value)
|
Ok(formatted_value)
|
||||||
}
|
}
|
||||||
|
None => Ok("".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a string with the cell content. If there is a formula returns the formula
|
/// Returns a string with the cell content. If there is a formula returns the formula
|
||||||
/// If the cell is empty returns the empty string
|
/// If the cell is empty returns the empty string
|
||||||
@@ -1647,15 +1652,53 @@ impl Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets cell to empty. Can be used to delete value without affecting style.
|
/// Removes the content of the cell but leaves the style.
|
||||||
pub fn set_cell_empty(&mut self, sheet: u32, row: i32, column: i32) -> Result<(), String> {
|
///
|
||||||
let worksheet = self.workbook.worksheet_mut(sheet)?;
|
/// See also:
|
||||||
worksheet.set_cell_empty(row, column);
|
/// * [Model::cell_clear_all()]
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use ironcalc_base::Model;
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
|
/// let (sheet, row, column) = (0, 1, 1);
|
||||||
|
/// model.set_user_input(sheet, row, column, "100$".to_string());
|
||||||
|
/// model.cell_clear_contents(sheet, row, column);
|
||||||
|
/// model.set_user_input(sheet, row, column, "10".to_string());
|
||||||
|
/// let result = model.get_formatted_cell_value(sheet, row, column)?;
|
||||||
|
/// assert_eq!(result, "10$".to_string());
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn cell_clear_contents(&mut self, sheet: u32, row: i32, column: i32) -> Result<(), String> {
|
||||||
|
self.workbook
|
||||||
|
.worksheet_mut(sheet)?
|
||||||
|
.cell_clear_contents(row, column);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes a cell by removing it from worksheet data.
|
/// Deletes a cell by removing it from worksheet data. All content and style is removed.
|
||||||
pub fn delete_cell(&mut self, sheet: u32, row: i32, column: i32) -> Result<(), String> {
|
///
|
||||||
|
/// See also:
|
||||||
|
/// * [Model::cell_clear_contents()]
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use ironcalc_base::Model;
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let mut model = Model::new_empty("model", "en", "UTC")?;
|
||||||
|
/// let (sheet, row, column) = (0, 1, 1);
|
||||||
|
/// model.set_user_input(sheet, row, column, "100$".to_string());
|
||||||
|
/// model.cell_clear_all(sheet, row, column);
|
||||||
|
/// model.set_user_input(sheet, row, column, "10".to_string());
|
||||||
|
/// let result = model.get_formatted_cell_value(sheet, row, column)?;
|
||||||
|
/// assert_eq!(result, "10".to_string());
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
pub fn cell_clear_all(&mut self, sheet: u32, row: i32, column: i32) -> Result<(), String> {
|
||||||
let worksheet = self.workbook.worksheet_mut(sheet)?;
|
let worksheet = self.workbook.worksheet_mut(sheet)?;
|
||||||
|
|
||||||
let sheet_data = &mut worksheet.sheet_data;
|
let sheet_data = &mut worksheet.sheet_data;
|
||||||
@@ -1682,9 +1725,8 @@ impl Model {
|
|||||||
if r.r == row {
|
if r.r == row {
|
||||||
if r.custom_format {
|
if r.custom_format {
|
||||||
return r.s;
|
return r.s;
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let cols = &self.workbook.worksheets[sheet as usize].cols;
|
let cols = &self.workbook.worksheets[sheet as usize].cols;
|
||||||
@@ -1718,8 +1760,22 @@ impl Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns data about the worksheets
|
||||||
|
pub fn get_sheets_info(&self) -> Vec<SheetInfo> {
|
||||||
|
self.workbook
|
||||||
|
.worksheets
|
||||||
|
.iter()
|
||||||
|
.map(|worksheet| SheetInfo {
|
||||||
|
name: worksheet.get_name(),
|
||||||
|
state: worksheet.state.to_string(),
|
||||||
|
color: worksheet.color.clone(),
|
||||||
|
sheet_id: worksheet.sheet_id,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns markup representation of the given `sheet`.
|
/// Returns markup representation of the given `sheet`.
|
||||||
pub fn sheet_markup(&self, sheet: u32) -> Result<String, String> {
|
pub fn get_sheet_markup(&self, sheet: u32) -> Result<String, String> {
|
||||||
let worksheet = self.workbook.worksheet(sheet)?;
|
let worksheet = self.workbook.worksheet(sheet)?;
|
||||||
let dimension = worksheet.dimension();
|
let dimension = worksheet.dimension();
|
||||||
|
|
||||||
@@ -1729,9 +1785,9 @@ impl Model {
|
|||||||
let mut row_markup: Vec<String> = Vec::new();
|
let mut row_markup: Vec<String> = Vec::new();
|
||||||
|
|
||||||
for column in 1..(dimension.max_column + 1) {
|
for column in 1..(dimension.max_column + 1) {
|
||||||
let mut cell_markup = match self.cell_formula(sheet, row, column)? {
|
let mut cell_markup = match self.get_cell_formula(sheet, row, column)? {
|
||||||
Some(formula) => formula,
|
Some(formula) => formula,
|
||||||
None => self.formatted_cell_value(sheet, row, column)?,
|
None => self.get_formatted_cell_value(sheet, row, column)?,
|
||||||
};
|
};
|
||||||
let style = self.get_style_for_cell(sheet, row, column);
|
let style = self.get_style_for_cell(sheet, row, column);
|
||||||
if style.font.b {
|
if style.font.b {
|
||||||
@@ -1770,7 +1826,7 @@ impl Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of frozen rows in `sheet`
|
/// Returns the number of frozen rows in `sheet`
|
||||||
pub fn get_frozen_rows(&self, sheet: u32) -> Result<i32, String> {
|
pub fn get_frozen_rows_count(&self, sheet: u32) -> Result<i32, String> {
|
||||||
if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
|
if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
|
||||||
Ok(worksheet.frozen_rows)
|
Ok(worksheet.frozen_rows)
|
||||||
} else {
|
} else {
|
||||||
@@ -1779,7 +1835,7 @@ impl Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the number of frozen columns in `sheet`
|
/// Return the number of frozen columns in `sheet`
|
||||||
pub fn get_frozen_columns(&self, sheet: u32) -> Result<i32, String> {
|
pub fn get_frozen_columns_count(&self, sheet: u32) -> Result<i32, String> {
|
||||||
if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
|
if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
|
||||||
Ok(worksheet.frozen_columns)
|
Ok(worksheet.frozen_columns)
|
||||||
} else {
|
} else {
|
||||||
@@ -1820,6 +1876,34 @@ impl Model {
|
|||||||
Err("Invalid sheet".to_string())
|
Err("Invalid sheet".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the width of a column
|
||||||
|
#[inline]
|
||||||
|
pub fn get_column_width(&self, sheet: u32, column: i32) -> Result<f64, String> {
|
||||||
|
self.workbook.worksheet(sheet)?.get_column_width(column)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the width of a column
|
||||||
|
#[inline]
|
||||||
|
pub fn set_column_width(&mut self, sheet: u32, column: i32, width: f64) -> Result<(), String> {
|
||||||
|
self.workbook
|
||||||
|
.worksheet_mut(sheet)?
|
||||||
|
.set_column_width(column, width)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the height of a row
|
||||||
|
#[inline]
|
||||||
|
pub fn get_row_height(&self, sheet: u32, row: i32) -> Result<f64, String> {
|
||||||
|
self.workbook.worksheet(sheet)?.row_height(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the height of a row
|
||||||
|
#[inline]
|
||||||
|
pub fn set_row_height(&mut self, sheet: u32, column: i32, height: f64) -> Result<(), String> {
|
||||||
|
self.workbook
|
||||||
|
.worksheet_mut(sheet)?
|
||||||
|
.set_row_height(column, height)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ impl Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reparses all formulas and defined names
|
// Reparses all formulas and defined names
|
||||||
fn reset_parsed_structures(&mut self) {
|
pub(crate) fn reset_parsed_structures(&mut self) {
|
||||||
self.parser
|
self.parser
|
||||||
.set_worksheets(self.workbook.get_worksheet_names());
|
.set_worksheets(self.workbook.get_worksheet_names());
|
||||||
self.parsed_formulas = vec![];
|
self.parsed_formulas = vec![];
|
||||||
@@ -134,10 +134,10 @@ impl Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a sheet with a automatically generated name
|
/// Adds a sheet with a automatically generated name
|
||||||
pub fn new_sheet(&mut self) {
|
pub fn new_sheet(&mut self) -> (String, u32) {
|
||||||
// First we find a name
|
// First we find a name
|
||||||
|
|
||||||
// TODO: When/if we support i18n the name could depend on the locale
|
// TODO: The name should depend on the locale
|
||||||
let base_name = "Sheet";
|
let base_name = "Sheet";
|
||||||
let base_name_uppercase = base_name.to_uppercase();
|
let base_name_uppercase = base_name.to_uppercase();
|
||||||
let mut index = 1;
|
let mut index = 1;
|
||||||
@@ -156,6 +156,7 @@ impl Model {
|
|||||||
let worksheet = Model::new_empty_worksheet(&sheet_name, sheet_id);
|
let worksheet = Model::new_empty_worksheet(&sheet_name, sheet_id);
|
||||||
self.workbook.worksheets.push(worksheet);
|
self.workbook.worksheets.push(worksheet);
|
||||||
self.reset_parsed_structures();
|
self.reset_parsed_structures();
|
||||||
|
(sheet_name, self.workbook.worksheets.len() as u32 - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a sheet with a particular index
|
/// Inserts a sheet with a particular index
|
||||||
@@ -223,10 +224,10 @@ impl Model {
|
|||||||
new_name: &str,
|
new_name: &str,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if !is_valid_sheet_name(new_name) {
|
if !is_valid_sheet_name(new_name) {
|
||||||
return Err(format!("Invalid name for a sheet: '{}'", new_name));
|
return Err(format!("Invalid name for a sheet: '{}'.", new_name));
|
||||||
}
|
}
|
||||||
if self.get_sheet_index_by_name(new_name).is_some() {
|
if self.get_sheet_index_by_name(new_name).is_some() {
|
||||||
return Err(format!("Sheet already exists: '{}'", new_name));
|
return Err(format!("Sheet already exists: '{}'.", new_name));
|
||||||
}
|
}
|
||||||
let worksheets = &self.workbook.worksheets;
|
let worksheets = &self.workbook.worksheets;
|
||||||
let sheet_count = worksheets.len() as u32;
|
let sheet_count = worksheets.len() as u32;
|
||||||
@@ -270,7 +271,7 @@ impl Model {
|
|||||||
if sheet_count == 1 {
|
if sheet_count == 1 {
|
||||||
return Err("Cannot delete only sheet".to_string());
|
return Err("Cannot delete only sheet".to_string());
|
||||||
};
|
};
|
||||||
if sheet_index > sheet_count {
|
if sheet_index >= sheet_count {
|
||||||
return Err("Sheet index too large".to_string());
|
return Err("Sheet index too large".to_string());
|
||||||
}
|
}
|
||||||
self.workbook.worksheets.remove(sheet_index as usize);
|
self.workbook.worksheets.remove(sheet_index as usize);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
mod test_actions;
|
mod test_actions;
|
||||||
mod test_binary_search;
|
mod test_binary_search;
|
||||||
mod test_cell;
|
mod test_cell;
|
||||||
|
mod test_cell_clear_contents;
|
||||||
mod test_circular_references;
|
mod test_circular_references;
|
||||||
mod test_column_width;
|
mod test_column_width;
|
||||||
mod test_criteria;
|
mod test_criteria;
|
||||||
@@ -28,9 +29,8 @@ mod test_frozen_rows_columns;
|
|||||||
mod test_general;
|
mod test_general;
|
||||||
mod test_math;
|
mod test_math;
|
||||||
mod test_metadata;
|
mod test_metadata;
|
||||||
mod test_model_delete_cell;
|
mod test_model_cell_clear_all;
|
||||||
mod test_model_is_empty_cell;
|
mod test_model_is_empty_cell;
|
||||||
mod test_model_set_cell_empty;
|
|
||||||
mod test_move_formula;
|
mod test_move_formula;
|
||||||
mod test_quote_prefix;
|
mod test_quote_prefix;
|
||||||
mod test_set_user_input;
|
mod test_set_user_input;
|
||||||
@@ -53,3 +53,4 @@ mod test_frozen_rows_and_columns;
|
|||||||
mod test_get_cell_content;
|
mod test_get_cell_content;
|
||||||
mod test_percentage;
|
mod test_percentage;
|
||||||
mod test_today;
|
mod test_today;
|
||||||
|
mod user_model;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use crate::constants::LAST_COLUMN;
|
use crate::constants::LAST_COLUMN;
|
||||||
use crate::model::Model;
|
use crate::model::Model;
|
||||||
use crate::test::util::new_empty_model;
|
use crate::test::util::new_empty_model;
|
||||||
|
use crate::types::Col;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_insert_columns() {
|
fn test_insert_columns() {
|
||||||
@@ -195,6 +196,250 @@ fn test_delete_columns() {
|
|||||||
assert_eq!(model._get_formula("A3"), *"=SUM(#REF!:K4)");
|
assert_eq!(model._get_formula("A3"), *"=SUM(#REF!:K4)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_column_width() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
let (sheet, column) = (0, 5);
|
||||||
|
let normal_width = model.get_column_width(sheet, column).unwrap();
|
||||||
|
// Set the width of one column to 5 times the normal width
|
||||||
|
assert!(model
|
||||||
|
.set_column_width(sheet, column, normal_width * 5.0)
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
|
// delete it
|
||||||
|
assert!(model.delete_columns(sheet, column, 1).is_ok());
|
||||||
|
|
||||||
|
// all the columns around have the expected width
|
||||||
|
assert_eq!(
|
||||||
|
model.get_column_width(sheet, column - 1).unwrap(),
|
||||||
|
normal_width
|
||||||
|
);
|
||||||
|
assert_eq!(model.get_column_width(sheet, column).unwrap(), normal_width);
|
||||||
|
assert_eq!(
|
||||||
|
model.get_column_width(sheet, column + 1).unwrap(),
|
||||||
|
normal_width
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// We set the style of columns 4 to 7 and delete column 4
|
||||||
|
// We check that columns 4 to 6 have the new style
|
||||||
|
fn test_delete_first_column_width() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model.workbook.worksheets[0].cols = vec![Col {
|
||||||
|
min: 4,
|
||||||
|
max: 7,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None,
|
||||||
|
}];
|
||||||
|
let (sheet, column) = (0, 4);
|
||||||
|
assert!(model.delete_columns(sheet, column, 1).is_ok());
|
||||||
|
let cols = &model.workbook.worksheets[0].cols;
|
||||||
|
assert_eq!(cols.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
cols[0],
|
||||||
|
Col {
|
||||||
|
min: 4,
|
||||||
|
max: 6,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Delete the last column in the range
|
||||||
|
fn test_delete_last_column_width() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model.workbook.worksheets[0].cols = vec![Col {
|
||||||
|
min: 4,
|
||||||
|
max: 7,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None,
|
||||||
|
}];
|
||||||
|
let (sheet, column) = (0, 7);
|
||||||
|
assert!(model.delete_columns(sheet, column, 1).is_ok());
|
||||||
|
let cols = &model.workbook.worksheets[0].cols;
|
||||||
|
assert_eq!(cols.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
cols[0],
|
||||||
|
Col {
|
||||||
|
min: 4,
|
||||||
|
max: 6,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Deletes columns at the end
|
||||||
|
fn test_delete_last_few_columns_width() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model.workbook.worksheets[0].cols = vec![Col {
|
||||||
|
min: 4,
|
||||||
|
max: 17,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None,
|
||||||
|
}];
|
||||||
|
let (sheet, column) = (0, 13);
|
||||||
|
assert!(model.delete_columns(sheet, column, 10).is_ok());
|
||||||
|
let cols = &model.workbook.worksheets[0].cols;
|
||||||
|
assert_eq!(cols.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
cols[0],
|
||||||
|
Col {
|
||||||
|
min: 4,
|
||||||
|
max: 12,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_columns_non_overlapping_left() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model.workbook.worksheets[0].cols = vec![Col {
|
||||||
|
min: 10,
|
||||||
|
max: 17,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None,
|
||||||
|
}];
|
||||||
|
let (sheet, column) = (0, 3);
|
||||||
|
assert!(model.delete_columns(sheet, column, 4).is_ok());
|
||||||
|
let cols = &model.workbook.worksheets[0].cols;
|
||||||
|
assert_eq!(cols.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
cols[0],
|
||||||
|
Col {
|
||||||
|
min: 6,
|
||||||
|
max: 13,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_columns_overlapping_left() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model.workbook.worksheets[0].cols = vec![Col {
|
||||||
|
min: 10,
|
||||||
|
max: 20,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None,
|
||||||
|
}];
|
||||||
|
let (sheet, column) = (0, 8);
|
||||||
|
assert!(model.delete_columns(sheet, column, 4).is_ok());
|
||||||
|
let cols = &model.workbook.worksheets[0].cols;
|
||||||
|
assert_eq!(cols.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
cols[0],
|
||||||
|
Col {
|
||||||
|
min: 8,
|
||||||
|
max: 16,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_columns_non_overlapping_right() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model.workbook.worksheets[0].cols = vec![Col {
|
||||||
|
min: 10,
|
||||||
|
max: 17,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None,
|
||||||
|
}];
|
||||||
|
let (sheet, column) = (0, 23);
|
||||||
|
assert!(model.delete_columns(sheet, column, 4).is_ok());
|
||||||
|
let cols = &model.workbook.worksheets[0].cols;
|
||||||
|
assert_eq!(cols.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
cols[0],
|
||||||
|
Col {
|
||||||
|
min: 10,
|
||||||
|
max: 17,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// deletes some columns in the middle of the range
|
||||||
|
fn test_delete_middle_column_width() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
// styled columns [4, 17]
|
||||||
|
model.workbook.worksheets[0].cols = vec![Col {
|
||||||
|
min: 4,
|
||||||
|
max: 17,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None,
|
||||||
|
}];
|
||||||
|
|
||||||
|
// deletes columns 10, 11, 12
|
||||||
|
let (sheet, column) = (0, 10);
|
||||||
|
assert!(model.delete_columns(sheet, column, 3).is_ok());
|
||||||
|
let cols = &model.workbook.worksheets[0].cols;
|
||||||
|
assert_eq!(cols.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
cols[0],
|
||||||
|
Col {
|
||||||
|
min: 4,
|
||||||
|
max: 14,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// the range is inside the deleted columns
|
||||||
|
fn delete_range_in_columns() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
// styled columns [6, 10]
|
||||||
|
model.workbook.worksheets[0].cols = vec![Col {
|
||||||
|
min: 6,
|
||||||
|
max: 10,
|
||||||
|
width: 300.0,
|
||||||
|
custom_width: true,
|
||||||
|
style: None,
|
||||||
|
}];
|
||||||
|
|
||||||
|
// deletes columns [4, 17]
|
||||||
|
let (sheet, column) = (0, 4);
|
||||||
|
assert!(model.delete_columns(sheet, column, 8).is_ok());
|
||||||
|
let cols = &model.workbook.worksheets[0].cols;
|
||||||
|
assert_eq!(cols.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_columns_error() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
let (sheet, column) = (0, 5);
|
||||||
|
assert!(model.delete_columns(sheet, column, -1).is_err());
|
||||||
|
assert!(model.delete_columns(sheet, column, 0).is_err());
|
||||||
|
assert!(model.delete_columns(sheet, column, 1).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_delete_rows() {
|
fn test_delete_rows() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
|
|||||||
@@ -2,25 +2,25 @@
|
|||||||
use crate::test::util::new_empty_model;
|
use crate::test::util::new_empty_model;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_cell_empty_non_existing_sheet() {
|
fn test_cell_clear_contents_non_existing_sheet() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.set_cell_empty(13, 1, 1),
|
model.cell_clear_contents(13, 1, 1),
|
||||||
Err("Invalid sheet index".to_string())
|
Err("Invalid sheet index".to_string())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_cell_empty_unset_cell() {
|
fn test_cell_clear_contents_unset_cell() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
model.set_cell_empty(0, 1, 1).unwrap();
|
model.cell_clear_contents(0, 1, 1).unwrap();
|
||||||
assert_eq!(model.is_empty_cell(0, 1, 1), Ok(true));
|
assert_eq!(model.is_empty_cell(0, 1, 1), Ok(true));
|
||||||
model.evaluate();
|
model.evaluate();
|
||||||
assert_eq!(model._get_text_at(0, 1, 1), "");
|
assert_eq!(model._get_text_at(0, 1, 1), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_cell_empty_with_value() {
|
fn test_cell_clear_contents_with_value() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
model._set("A1", "hello");
|
model._set("A1", "hello");
|
||||||
model.evaluate();
|
model.evaluate();
|
||||||
@@ -28,7 +28,7 @@ fn test_set_cell_empty_with_value() {
|
|||||||
assert_eq!(model._get_text_at(0, 1, 1), "hello");
|
assert_eq!(model._get_text_at(0, 1, 1), "hello");
|
||||||
assert_eq!(model.is_empty_cell(0, 1, 1), Ok(false));
|
assert_eq!(model.is_empty_cell(0, 1, 1), Ok(false));
|
||||||
|
|
||||||
model.set_cell_empty(0, 1, 1).unwrap();
|
model.cell_clear_contents(0, 1, 1).unwrap();
|
||||||
model.evaluate();
|
model.evaluate();
|
||||||
|
|
||||||
assert_eq!(model._get_text_at(0, 1, 1), "");
|
assert_eq!(model._get_text_at(0, 1, 1), "");
|
||||||
@@ -36,7 +36,7 @@ fn test_set_cell_empty_with_value() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_cell_empty_referenced_elsewhere() {
|
fn test_cell_clear_contents_referenced_elsewhere() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
model._set("A1", "35");
|
model._set("A1", "35");
|
||||||
model._set("A2", "=2*A1");
|
model._set("A2", "=2*A1");
|
||||||
@@ -47,7 +47,7 @@ fn test_set_cell_empty_referenced_elsewhere() {
|
|||||||
assert_eq!(model.is_empty_cell(0, 1, 1), Ok(false));
|
assert_eq!(model.is_empty_cell(0, 1, 1), Ok(false));
|
||||||
assert_eq!(model.is_empty_cell(0, 2, 1), Ok(false));
|
assert_eq!(model.is_empty_cell(0, 2, 1), Ok(false));
|
||||||
|
|
||||||
model.set_cell_empty(0, 1, 1).unwrap();
|
model.cell_clear_contents(0, 1, 1).unwrap();
|
||||||
model.evaluate();
|
model.evaluate();
|
||||||
|
|
||||||
assert_eq!(model._get_text_at(0, 1, 1), "");
|
assert_eq!(model._get_text_at(0, 1, 1), "");
|
||||||
@@ -23,9 +23,9 @@ fn test_column_width() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(model.workbook.worksheets[0].cols.len(), 3);
|
assert_eq!(model.workbook.worksheets[0].cols.len(), 3);
|
||||||
let worksheet = model.workbook.worksheet(0).unwrap();
|
let worksheet = model.workbook.worksheet(0).unwrap();
|
||||||
assert!((worksheet.column_width(1).unwrap() - DEFAULT_COLUMN_WIDTH).abs() < f64::EPSILON);
|
assert!((worksheet.get_column_width(1).unwrap() - DEFAULT_COLUMN_WIDTH).abs() < f64::EPSILON);
|
||||||
assert!((worksheet.column_width(2).unwrap() - 30.0).abs() < f64::EPSILON);
|
assert!((worksheet.get_column_width(2).unwrap() - 30.0).abs() < f64::EPSILON);
|
||||||
assert!((worksheet.column_width(3).unwrap() - DEFAULT_COLUMN_WIDTH).abs() < f64::EPSILON);
|
assert!((worksheet.get_column_width(3).unwrap() - DEFAULT_COLUMN_WIDTH).abs() < f64::EPSILON);
|
||||||
assert_eq!(model.get_cell_style_index(0, 23, 2), 6);
|
assert_eq!(model.get_cell_style_index(0, 23, 2), 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,9 +48,11 @@ fn test_column_width_lower_edge() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(model.workbook.worksheets[0].cols.len(), 2);
|
assert_eq!(model.workbook.worksheets[0].cols.len(), 2);
|
||||||
let worksheet = model.workbook.worksheet(0).unwrap();
|
let worksheet = model.workbook.worksheet(0).unwrap();
|
||||||
assert!((worksheet.column_width(4).unwrap() - DEFAULT_COLUMN_WIDTH).abs() < f64::EPSILON);
|
assert!((worksheet.get_column_width(4).unwrap() - DEFAULT_COLUMN_WIDTH).abs() < f64::EPSILON);
|
||||||
assert!((worksheet.column_width(5).unwrap() - 30.0).abs() < f64::EPSILON);
|
assert!((worksheet.get_column_width(5).unwrap() - 30.0).abs() < f64::EPSILON);
|
||||||
assert!((worksheet.column_width(6).unwrap() - 10.0 * COLUMN_WIDTH_FACTOR).abs() < f64::EPSILON);
|
assert!(
|
||||||
|
(worksheet.get_column_width(6).unwrap() - 10.0 * COLUMN_WIDTH_FACTOR).abs() < f64::EPSILON
|
||||||
|
);
|
||||||
assert_eq!(model.get_cell_style_index(0, 23, 5), 1);
|
assert_eq!(model.get_cell_style_index(0, 23, 5), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,9 +76,9 @@ fn test_column_width_higher_edge() {
|
|||||||
assert_eq!(model.workbook.worksheets[0].cols.len(), 2);
|
assert_eq!(model.workbook.worksheets[0].cols.len(), 2);
|
||||||
let worksheet = model.workbook.worksheet(0).unwrap();
|
let worksheet = model.workbook.worksheet(0).unwrap();
|
||||||
assert!(
|
assert!(
|
||||||
(worksheet.column_width(15).unwrap() - 10.0 * COLUMN_WIDTH_FACTOR).abs() < f64::EPSILON
|
(worksheet.get_column_width(15).unwrap() - 10.0 * COLUMN_WIDTH_FACTOR).abs() < f64::EPSILON
|
||||||
);
|
);
|
||||||
assert!((worksheet.column_width(16).unwrap() - 30.0).abs() < f64::EPSILON);
|
assert!((worksheet.get_column_width(16).unwrap() - 30.0).abs() < f64::EPSILON);
|
||||||
assert!((worksheet.column_width(17).unwrap() - DEFAULT_COLUMN_WIDTH).abs() < f64::EPSILON);
|
assert!((worksheet.get_column_width(17).unwrap() - DEFAULT_COLUMN_WIDTH).abs() < f64::EPSILON);
|
||||||
assert_eq!(model.get_cell_style_index(0, 23, 16), 1);
|
assert_eq!(model.get_cell_style_index(0, 23, 16), 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,34 +8,37 @@ use crate::{
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_empty_model() {
|
fn test_empty_model() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
assert_eq!(model.get_frozen_rows(0), Ok(0));
|
assert_eq!(model.get_frozen_rows_count(0), Ok(0));
|
||||||
assert_eq!(model.get_frozen_columns(0), Ok(0));
|
assert_eq!(model.get_frozen_columns_count(0), Ok(0));
|
||||||
|
|
||||||
let e = model.set_frozen_rows(0, 3);
|
let e = model.set_frozen_rows(0, 3);
|
||||||
assert!(e.is_ok());
|
assert!(e.is_ok());
|
||||||
assert_eq!(model.get_frozen_rows(0), Ok(3));
|
assert_eq!(model.get_frozen_rows_count(0), Ok(3));
|
||||||
assert_eq!(model.get_frozen_columns(0), Ok(0));
|
assert_eq!(model.get_frozen_columns_count(0), Ok(0));
|
||||||
|
|
||||||
let e = model.set_frozen_columns(0, 53);
|
let e = model.set_frozen_columns(0, 53);
|
||||||
assert!(e.is_ok());
|
assert!(e.is_ok());
|
||||||
assert_eq!(model.get_frozen_rows(0), Ok(3));
|
assert_eq!(model.get_frozen_rows_count(0), Ok(3));
|
||||||
assert_eq!(model.get_frozen_columns(0), Ok(53));
|
assert_eq!(model.get_frozen_columns_count(0), Ok(53));
|
||||||
|
|
||||||
// Set them back to zero
|
// Set them back to zero
|
||||||
let e = model.set_frozen_rows(0, 0);
|
let e = model.set_frozen_rows(0, 0);
|
||||||
assert!(e.is_ok());
|
assert!(e.is_ok());
|
||||||
let e = model.set_frozen_columns(0, 0);
|
let e = model.set_frozen_columns(0, 0);
|
||||||
assert!(e.is_ok());
|
assert!(e.is_ok());
|
||||||
assert_eq!(model.get_frozen_rows(0), Ok(0));
|
assert_eq!(model.get_frozen_rows_count(0), Ok(0));
|
||||||
assert_eq!(model.get_frozen_columns(0), Ok(0));
|
assert_eq!(model.get_frozen_columns_count(0), Ok(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_sheet() {
|
fn test_invalid_sheet() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
assert_eq!(model.get_frozen_rows(1), Err("Invalid sheet".to_string()));
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.get_frozen_columns(3),
|
model.get_frozen_rows_count(1),
|
||||||
|
Err("Invalid sheet".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
model.get_frozen_columns_count(3),
|
||||||
Err("Invalid sheet".to_string())
|
Err("Invalid sheet".to_string())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -411,11 +411,11 @@ fn test_get_formatted_cell_value() {
|
|||||||
|
|
||||||
model.evaluate();
|
model.evaluate();
|
||||||
|
|
||||||
assert_eq!(model.formatted_cell_value(0, 1, 1).unwrap(), "foobar");
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1).unwrap(), "foobar");
|
||||||
assert_eq!(model.formatted_cell_value(0, 2, 1).unwrap(), "TRUE");
|
assert_eq!(model.get_formatted_cell_value(0, 2, 1).unwrap(), "TRUE");
|
||||||
assert_eq!(model.formatted_cell_value(0, 3, 1).unwrap(), "");
|
assert_eq!(model.get_formatted_cell_value(0, 3, 1).unwrap(), "");
|
||||||
assert_eq!(model.formatted_cell_value(0, 4, 1).unwrap(), "123.456");
|
assert_eq!(model.get_formatted_cell_value(0, 4, 1).unwrap(), "123.456");
|
||||||
assert_eq!(model.formatted_cell_value(0, 5, 1).unwrap(), "$123.46");
|
assert_eq!(model.get_formatted_cell_value(0, 5, 1).unwrap(), "$123.46");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -426,20 +426,20 @@ fn test_cell_formula() {
|
|||||||
model.evaluate();
|
model.evaluate();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.cell_formula(0, 1, 1), // A1
|
model.get_cell_formula(0, 1, 1), // A1
|
||||||
Ok(Some("=1+2+3".to_string())),
|
Ok(Some("=1+2+3".to_string())),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.cell_formula(0, 2, 1), // A2
|
model.get_cell_formula(0, 2, 1), // A2
|
||||||
Ok(None),
|
Ok(None),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.cell_formula(0, 3, 1), // A3 - empty cell
|
model.get_cell_formula(0, 3, 1), // A3 - empty cell
|
||||||
Ok(None),
|
Ok(None),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.cell_formula(42, 1, 1),
|
model.get_cell_formula(42, 1, 1),
|
||||||
Err("Invalid sheet index".to_string()),
|
Err("Invalid sheet index".to_string()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -453,16 +453,16 @@ fn test_xlfn() {
|
|||||||
model.evaluate();
|
model.evaluate();
|
||||||
// Only modern formulas strip the '_xlfn.'
|
// Only modern formulas strip the '_xlfn.'
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.cell_formula(0, 1, 1).unwrap(),
|
model.get_cell_formula(0, 1, 1).unwrap(),
|
||||||
Some("=_xlfn.SIN(1)".to_string())
|
Some("=_xlfn.SIN(1)".to_string())
|
||||||
);
|
);
|
||||||
// unknown formulas keep the '_xlfn.' prefix
|
// unknown formulas keep the '_xlfn.' prefix
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.cell_formula(0, 2, 1).unwrap(),
|
model.get_cell_formula(0, 2, 1).unwrap(),
|
||||||
Some("=_xlfn.SINY(1)".to_string())
|
Some("=_xlfn.SINY(1)".to_string())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.cell_formula(0, 3, 1).unwrap(),
|
model.get_cell_formula(0, 3, 1).unwrap(),
|
||||||
Some("=CONCAT(3,4)".to_string())
|
Some("=CONCAT(3,4)".to_string())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -474,11 +474,11 @@ fn test_letter_case() {
|
|||||||
model._set("A2", "=sIn(2)");
|
model._set("A2", "=sIn(2)");
|
||||||
model.evaluate();
|
model.evaluate();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.cell_formula(0, 1, 1).unwrap(),
|
model.get_cell_formula(0, 1, 1).unwrap(),
|
||||||
Some("=SIN(1)".to_string())
|
Some("=SIN(1)".to_string())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.cell_formula(0, 2, 1).unwrap(),
|
model.get_cell_formula(0, 2, 1).unwrap(),
|
||||||
Some("=SIN(2)".to_string())
|
Some("=SIN(2)".to_string())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,22 @@
|
|||||||
use crate::test::util::new_empty_model;
|
use crate::test::util::new_empty_model;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_delete_cell_non_existing_sheet() {
|
fn test_cell_clear_all_non_existing_sheet() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.delete_cell(13, 1, 1),
|
model.cell_clear_all(13, 1, 1),
|
||||||
Err("Invalid sheet index".to_string())
|
Err("Invalid sheet index".to_string())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_delete_cell_unset_cell() {
|
fn test_cell_clear_all_unset_cell() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
assert!(model.delete_cell(0, 1, 1).is_ok());
|
assert!(model.cell_clear_all(0, 1, 1).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_delete_cell_with_value() {
|
fn test_cell_clear_all_with_value() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
model._set("A1", "hello");
|
model._set("A1", "hello");
|
||||||
model.evaluate();
|
model.evaluate();
|
||||||
@@ -25,7 +25,7 @@ fn test_delete_cell_with_value() {
|
|||||||
assert_eq!(model._get_text_at(0, 1, 1), "hello");
|
assert_eq!(model._get_text_at(0, 1, 1), "hello");
|
||||||
assert_eq!(model.is_empty_cell(0, 1, 1), Ok(false));
|
assert_eq!(model.is_empty_cell(0, 1, 1), Ok(false));
|
||||||
|
|
||||||
model.delete_cell(0, 1, 1).unwrap();
|
model.cell_clear_all(0, 1, 1).unwrap();
|
||||||
model.evaluate();
|
model.evaluate();
|
||||||
|
|
||||||
assert_eq!(model._get_text_at(0, 1, 1), "");
|
assert_eq!(model._get_text_at(0, 1, 1), "");
|
||||||
@@ -33,7 +33,7 @@ fn test_delete_cell_with_value() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_delete_cell_referenced_elsewhere() {
|
fn test_cell_clear_all_referenced_elsewhere() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
model._set("A1", "35");
|
model._set("A1", "35");
|
||||||
model._set("A2", "=2*A1");
|
model._set("A2", "=2*A1");
|
||||||
@@ -44,7 +44,7 @@ fn test_delete_cell_referenced_elsewhere() {
|
|||||||
assert_eq!(model.is_empty_cell(0, 1, 1), Ok(false));
|
assert_eq!(model.is_empty_cell(0, 1, 1), Ok(false));
|
||||||
assert_eq!(model.is_empty_cell(0, 2, 1), Ok(false));
|
assert_eq!(model.is_empty_cell(0, 2, 1), Ok(false));
|
||||||
|
|
||||||
model.delete_cell(0, 1, 1).unwrap();
|
model.cell_clear_all(0, 1, 1).unwrap();
|
||||||
model.evaluate();
|
model.evaluate();
|
||||||
|
|
||||||
assert_eq!(model._get_text_at(0, 1, 1), "");
|
assert_eq!(model._get_text_at(0, 1, 1), "");
|
||||||
@@ -16,7 +16,7 @@ fn test_is_empty_cell() {
|
|||||||
assert!(model.is_empty_cell(0, 3, 1).unwrap());
|
assert!(model.is_empty_cell(0, 3, 1).unwrap());
|
||||||
model.set_user_input(0, 3, 1, "Hello World".to_string());
|
model.set_user_input(0, 3, 1, "Hello World".to_string());
|
||||||
assert!(!model.is_empty_cell(0, 3, 1).unwrap());
|
assert!(!model.is_empty_cell(0, 3, 1).unwrap());
|
||||||
model.set_cell_empty(0, 3, 1).unwrap();
|
model.cell_clear_contents(0, 3, 1).unwrap();
|
||||||
assert!(model.is_empty_cell(0, 3, 1).unwrap());
|
assert!(model.is_empty_cell(0, 3, 1).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ fn test_sheet_markup() {
|
|||||||
model.set_cell_style(0, 4, 1, &style).unwrap();
|
model.set_cell_style(0, 4, 1, &style).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.sheet_markup(0),
|
model.get_sheet_markup(0),
|
||||||
Ok("**Item**|**Cost**\nRent|$600\nElectricity|$200\n**Total**|=SUM(B2:B3)".to_string()),
|
Ok("**Item**|**Cost**\nRent|$600\nElectricity|$200\n**Total**|=SUM(B2:B3)".to_string()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,3 +236,11 @@ fn test_delete_sheet_by_index() {
|
|||||||
assert_eq!(model.workbook.get_worksheet_names(), ["Sheet2"]);
|
assert_eq!(model.workbook.get_worksheet_names(), ["Sheet2"]);
|
||||||
assert_eq!(model._get_text("Sheet2!A1"), "#REF!");
|
assert_eq!(model._get_text("Sheet2!A1"), "#REF!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delete_sheet_error() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model.new_sheet();
|
||||||
|
assert!(model.delete_sheet(2).is_err());
|
||||||
|
assert!(model.delete_sheet(1).is_ok());
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::{test::util::new_empty_model, types::SheetInfo};
|
|||||||
#[test]
|
#[test]
|
||||||
fn workbook_worksheets_info() {
|
fn workbook_worksheets_info() {
|
||||||
let model = new_empty_model();
|
let model = new_empty_model();
|
||||||
let sheets_info = model.workbook.get_worksheets_info();
|
let sheets_info = model.get_sheets_info();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sheets_info[0],
|
sheets_info[0],
|
||||||
SheetInfo {
|
SheetInfo {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ fn test_worksheet_dimension_single_cell() {
|
|||||||
fn test_worksheet_dimension_single_cell_set_empty() {
|
fn test_worksheet_dimension_single_cell_set_empty() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
model._set("W11", "1");
|
model._set("W11", "1");
|
||||||
model.set_cell_empty(0, 11, 23).unwrap();
|
model.cell_clear_contents(0, 11, 23).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
@@ -55,7 +55,7 @@ fn test_worksheet_dimension_single_cell_set_empty() {
|
|||||||
fn test_worksheet_dimension_single_cell_deleted() {
|
fn test_worksheet_dimension_single_cell_deleted() {
|
||||||
let mut model = new_empty_model();
|
let mut model = new_empty_model();
|
||||||
model._set("W11", "1");
|
model._set("W11", "1");
|
||||||
model.delete_cell(0, 11, 23).unwrap();
|
model.cell_clear_all(0, 11, 23).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
@@ -75,7 +75,7 @@ fn test_worksheet_dimension_multiple_cells() {
|
|||||||
model._set("AA17", "1");
|
model._set("AA17", "1");
|
||||||
model._set("G17", "1");
|
model._set("G17", "1");
|
||||||
model._set("B19", "1");
|
model._set("B19", "1");
|
||||||
model.delete_cell(0, 11, 23).unwrap();
|
model.cell_clear_all(0, 11, 23).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.workbook.worksheet(0).unwrap().dimension(),
|
model.workbook.worksheet(0).unwrap().dimension(),
|
||||||
WorksheetDimension {
|
WorksheetDimension {
|
||||||
|
|||||||
9
base/src/test/user_model/mod.rs
Normal file
9
base/src/test/user_model/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
mod test_add_delete_sheets;
|
||||||
|
mod test_clear_cells;
|
||||||
|
mod test_diff_queue;
|
||||||
|
mod test_evaluation;
|
||||||
|
mod test_general;
|
||||||
|
mod test_rename_sheet;
|
||||||
|
mod test_row_column;
|
||||||
|
mod test_styles;
|
||||||
|
mod test_undo_redo;
|
||||||
58
base/src/test/user_model/test_add_delete_sheets.rs
Normal file
58
base/src/test/user_model/test_add_delete_sheets.rs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::{constants::DEFAULT_COLUMN_WIDTH, UserModel};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_undo_redo() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model.new_sheet();
|
||||||
|
model.set_user_input(1, 1, 1, "=1 + 1").unwrap();
|
||||||
|
model.set_user_input(1, 1, 2, "=A1*3").unwrap();
|
||||||
|
model
|
||||||
|
.set_column_width(1, 5, 5.0 * DEFAULT_COLUMN_WIDTH)
|
||||||
|
.unwrap();
|
||||||
|
model.new_sheet();
|
||||||
|
model.set_user_input(2, 1, 1, "=Sheet2!B1").unwrap();
|
||||||
|
|
||||||
|
model.undo().unwrap();
|
||||||
|
model.undo().unwrap();
|
||||||
|
|
||||||
|
assert!(model.get_formatted_cell_value(2, 1, 1).is_err());
|
||||||
|
|
||||||
|
model.redo().unwrap();
|
||||||
|
model.redo().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(model.get_formatted_cell_value(2, 1, 1), Ok("6".to_string()));
|
||||||
|
|
||||||
|
model.delete_sheet(1).unwrap();
|
||||||
|
|
||||||
|
assert!(!model.can_undo());
|
||||||
|
assert!(!model.can_redo());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_sheet_propagates() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model.new_sheet();
|
||||||
|
|
||||||
|
let send_queue = model.flush_send_queue();
|
||||||
|
|
||||||
|
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
let sheets_info = model2.get_sheets_info();
|
||||||
|
assert_eq!(sheets_info.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delete_sheet_propagates() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model.new_sheet();
|
||||||
|
model.delete_sheet(0).unwrap();
|
||||||
|
|
||||||
|
let send_queue = model.flush_send_queue();
|
||||||
|
|
||||||
|
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
let sheets_info = model2.get_sheets_info();
|
||||||
|
assert_eq!(sheets_info.len(), 1);
|
||||||
|
}
|
||||||
91
base/src/test/user_model/test_clear_cells.rs
Normal file
91
base/src/test/user_model/test_clear_cells.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::{expressions::types::Area, UserModel};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model.set_user_input(0, 1, 1, "100$").unwrap();
|
||||||
|
model
|
||||||
|
.range_clear_contents(&Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
|
||||||
|
model.undo().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 1, 1),
|
||||||
|
Ok("100$".to_string())
|
||||||
|
);
|
||||||
|
model.redo().unwrap();
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
|
||||||
|
|
||||||
|
model.set_user_input(0, 1, 1, "300").unwrap();
|
||||||
|
// clear contents keeps the formatting
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 1, 1),
|
||||||
|
Ok("300$".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
model
|
||||||
|
.range_clear_all(&Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
|
||||||
|
model.undo().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 1, 1),
|
||||||
|
Ok("300$".to_string())
|
||||||
|
);
|
||||||
|
model.redo().unwrap();
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
|
||||||
|
model.set_user_input(0, 1, 1, "400").unwrap();
|
||||||
|
// clear contents keeps the formatting
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 1, 1),
|
||||||
|
Ok("400".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn clear_empty_cell() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model
|
||||||
|
.range_clear_contents(&Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
|
||||||
|
model.undo().unwrap();
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn clear_all_empty_cell() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model
|
||||||
|
.range_clear_all(&Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
|
||||||
|
model.undo().unwrap();
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
|
||||||
|
}
|
||||||
159
base/src/test/user_model/test_diff_queue.rs
Normal file
159
base/src/test/user_model/test_diff_queue.rs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
use crate::{
|
||||||
|
constants::{DEFAULT_COLUMN_WIDTH, DEFAULT_ROW_HEIGHT},
|
||||||
|
test::util::new_empty_model,
|
||||||
|
UserModel,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn send_queue() {
|
||||||
|
let mut model1 = UserModel::from_model(new_empty_model());
|
||||||
|
let width = model1.get_column_width(0, 3).unwrap() * 3.0;
|
||||||
|
model1.set_column_width(0, 3, width).unwrap();
|
||||||
|
model1.set_user_input(0, 1, 2, "Hello IronCalc!").unwrap();
|
||||||
|
let send_queue = model1.flush_send_queue();
|
||||||
|
|
||||||
|
let mut model2 = UserModel::from_model(new_empty_model());
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(model2.get_column_width(0, 3), Ok(width));
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_formatted_cell_value(0, 1, 2),
|
||||||
|
Ok("Hello IronCalc!".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn apply_external_diffs_wrong_str() {
|
||||||
|
let mut model1 = UserModel::from_model(new_empty_model());
|
||||||
|
assert!(model1.apply_external_diffs("invalid").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn queue_undo_redo() {
|
||||||
|
let mut model1 = UserModel::from_model(new_empty_model());
|
||||||
|
let width = model1.get_column_width(0, 3).unwrap() * 3.0;
|
||||||
|
model1.set_column_width(0, 3, width).unwrap();
|
||||||
|
model1.set_user_input(0, 1, 2, "Hello IronCalc!").unwrap();
|
||||||
|
assert!(model1.undo().is_ok());
|
||||||
|
assert!(model1.redo().is_ok());
|
||||||
|
let send_queue = model1.flush_send_queue();
|
||||||
|
|
||||||
|
let mut model2 = UserModel::from_model(new_empty_model());
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(model2.get_column_width(0, 3), Ok(width));
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_formatted_cell_value(0, 1, 2),
|
||||||
|
Ok("Hello IronCalc!".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn queue_undo_redo_multiple() {
|
||||||
|
let mut model1 = UserModel::from_model(new_empty_model());
|
||||||
|
|
||||||
|
// do a bunch of things
|
||||||
|
model1.set_frozen_columns_count(0, 5).unwrap();
|
||||||
|
model1.set_frozen_rows_count(0, 6).unwrap();
|
||||||
|
model1.set_column_width(0, 7, 300.0).unwrap();
|
||||||
|
model1.set_row_height(0, 23, 123.0).unwrap();
|
||||||
|
model1.set_user_input(0, 55, 55, "=42+8").unwrap();
|
||||||
|
|
||||||
|
for row in 1..5 {
|
||||||
|
model1.set_user_input(0, row, 17, "=ROW()").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
model1.insert_row(0, 3).unwrap();
|
||||||
|
model1.insert_row(0, 3).unwrap();
|
||||||
|
|
||||||
|
// undo al of them
|
||||||
|
while model1.can_undo() {
|
||||||
|
model1.undo().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// check it is an empty model
|
||||||
|
assert_eq!(model1.get_frozen_columns_count(0), Ok(0));
|
||||||
|
assert_eq!(model1.get_frozen_rows_count(0), Ok(0));
|
||||||
|
assert_eq!(model1.get_column_width(0, 7), Ok(DEFAULT_COLUMN_WIDTH));
|
||||||
|
assert_eq!(
|
||||||
|
model1.get_formatted_cell_value(0, 55, 55),
|
||||||
|
Ok("".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(model1.get_row_height(0, 23), Ok(DEFAULT_ROW_HEIGHT));
|
||||||
|
assert_eq!(
|
||||||
|
model1.get_formatted_cell_value(0, 57, 55),
|
||||||
|
Ok("".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(model1.get_row_height(0, 25), Ok(DEFAULT_ROW_HEIGHT));
|
||||||
|
|
||||||
|
// redo all of them
|
||||||
|
while model1.can_redo() {
|
||||||
|
model1.redo().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// now send all this to a new model
|
||||||
|
let send_queue = model1.flush_send_queue();
|
||||||
|
let mut model2 = UserModel::from_model(new_empty_model());
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
|
||||||
|
// Check everything is as expected
|
||||||
|
assert_eq!(model2.get_frozen_columns_count(0), Ok(5));
|
||||||
|
assert_eq!(model2.get_frozen_rows_count(0), Ok(6));
|
||||||
|
assert_eq!(model2.get_column_width(0, 7), Ok(300.0));
|
||||||
|
// I inserted two rows
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_formatted_cell_value(0, 57, 55),
|
||||||
|
Ok("50".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(model2.get_row_height(0, 25), Ok(123.0));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_formatted_cell_value(0, 1, 17),
|
||||||
|
Ok("1".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_formatted_cell_value(0, 2, 17),
|
||||||
|
Ok("2".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_formatted_cell_value(0, 3, 17),
|
||||||
|
Ok("".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_formatted_cell_value(0, 4, 17),
|
||||||
|
Ok("".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_formatted_cell_value(0, 5, 17),
|
||||||
|
Ok("5".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_formatted_cell_value(0, 6, 17),
|
||||||
|
Ok("6".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_sheet() {
|
||||||
|
let mut model1 = UserModel::from_model(new_empty_model());
|
||||||
|
model1.new_sheet();
|
||||||
|
model1.set_user_input(0, 1, 1, "42").unwrap();
|
||||||
|
model1.set_user_input(1, 1, 1, "=Sheet1!A1*2").unwrap();
|
||||||
|
|
||||||
|
let send_queue = model1.flush_send_queue();
|
||||||
|
let mut model2 = UserModel::from_model(new_empty_model());
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_formatted_cell_value(1, 1, 1),
|
||||||
|
Ok("84".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wrong_diffs_handled() {
|
||||||
|
let mut model = UserModel::from_model(new_empty_model());
|
||||||
|
assert!(model.apply_external_diffs("Hello world").is_err());
|
||||||
|
}
|
||||||
31
base/src/test/user_model/test_evaluation.rs
Normal file
31
base/src/test/user_model/test_evaluation.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::UserModel;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn model_evaluates_automatically() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model.set_user_input(0, 1, 1, "=1 + 1").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("2".to_string()));
|
||||||
|
assert_eq!(model.get_cell_content(0, 1, 1), Ok("=1+1".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pause_resume_evaluation() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model.pause_evaluation();
|
||||||
|
model.set_user_input(0, 1, 1, "=1+1").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 1, 1),
|
||||||
|
Ok("#ERROR!".to_string())
|
||||||
|
);
|
||||||
|
model.evaluate();
|
||||||
|
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("2".to_string()));
|
||||||
|
assert_eq!(model.get_cell_content(0, 1, 1), Ok("=1+1".to_string()));
|
||||||
|
|
||||||
|
model.resume_evaluation();
|
||||||
|
model.set_user_input(0, 2, 1, "=1+4").unwrap();
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 2, 1), Ok("5".to_string()));
|
||||||
|
}
|
||||||
103
base/src/test/user_model/test_general.rs
Normal file
103
base/src/test/user_model/test_general.rs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::constants::{LAST_COLUMN, LAST_ROW};
|
||||||
|
use crate::test::util::new_empty_model;
|
||||||
|
use crate::UserModel;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_user_input_errors() {
|
||||||
|
let model = new_empty_model();
|
||||||
|
let mut model = UserModel::from_model(model);
|
||||||
|
// Wrong sheet
|
||||||
|
assert!(model.set_user_input(1, 1, 1, "1").is_err());
|
||||||
|
// Wrong row
|
||||||
|
assert!(model.set_user_input(0, 0, 1, "1").is_err());
|
||||||
|
// Wrong column
|
||||||
|
assert!(model.set_user_input(0, 1, 0, "1").is_err());
|
||||||
|
// row too large
|
||||||
|
assert!(model.set_user_input(0, LAST_ROW, 1, "1").is_ok());
|
||||||
|
assert!(model.set_user_input(0, LAST_ROW + 1, 1, "1").is_err());
|
||||||
|
// column too large
|
||||||
|
assert!(model.set_user_input(0, 1, LAST_COLUMN, "1").is_ok());
|
||||||
|
assert!(model.set_user_input(0, 1, LAST_COLUMN + 1, "1").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insert_remove_rows() {
|
||||||
|
let model = new_empty_model();
|
||||||
|
let mut model = UserModel::from_model(model);
|
||||||
|
let height = model.get_row_height(0, 5).unwrap();
|
||||||
|
|
||||||
|
// Insert some data in row 5 (and change the style)
|
||||||
|
assert!(model.set_user_input(0, 5, 1, "100$").is_ok());
|
||||||
|
// Change the height of the column
|
||||||
|
assert!(model.set_row_height(0, 5, 3.0 * height).is_ok());
|
||||||
|
|
||||||
|
// remove the row
|
||||||
|
assert!(model.delete_row(0, 5).is_ok());
|
||||||
|
// Row 5 has now the normal height
|
||||||
|
assert_eq!(model.get_row_height(0, 5), Ok(height));
|
||||||
|
// There is no value in A5
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 5, 1), Ok("".to_string()));
|
||||||
|
// Setting a value will not format it
|
||||||
|
assert!(model.set_user_input(0, 5, 1, "125").is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 5, 1),
|
||||||
|
Ok("125".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
// undo twice
|
||||||
|
assert!(model.undo().is_ok());
|
||||||
|
assert!(model.undo().is_ok());
|
||||||
|
|
||||||
|
assert_eq!(model.get_row_height(0, 5), Ok(3.0 * height));
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 5, 1),
|
||||||
|
Ok("100$".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insert_remove_columns() {
|
||||||
|
let model = new_empty_model();
|
||||||
|
let mut model = UserModel::from_model(model);
|
||||||
|
// column E
|
||||||
|
let column_width = model.get_column_width(0, 5).unwrap();
|
||||||
|
println!("{column_width}");
|
||||||
|
|
||||||
|
// Insert some data in row 5 (and change the style) in E1
|
||||||
|
assert!(model.set_user_input(0, 1, 5, "100$").is_ok());
|
||||||
|
// Change the width of the column
|
||||||
|
assert!(model.set_column_width(0, 5, 3.0 * column_width).is_ok());
|
||||||
|
assert_eq!(model.get_column_width(0, 5).unwrap(), 3.0 * column_width);
|
||||||
|
|
||||||
|
// remove the column
|
||||||
|
assert!(model.delete_column(0, 5).is_ok());
|
||||||
|
// Column 5 has now the normal width
|
||||||
|
assert_eq!(model.get_column_width(0, 5), Ok(column_width));
|
||||||
|
// There is no value in E5
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 5), Ok("".to_string()));
|
||||||
|
// Setting a value will not format it
|
||||||
|
assert!(model.set_user_input(0, 1, 5, "125").is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 1, 5),
|
||||||
|
Ok("125".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
// undo twice (set_user_input and delete_column)
|
||||||
|
assert!(model.undo().is_ok());
|
||||||
|
assert!(model.undo().is_ok());
|
||||||
|
|
||||||
|
assert_eq!(model.get_column_width(0, 5), Ok(3.0 * column_width));
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 1, 5),
|
||||||
|
Ok("100$".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delete_remove_cell() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let (sheet, row, column) = (0, 1, 1);
|
||||||
|
model.set_user_input(sheet, row, column, "100$").unwrap();
|
||||||
|
}
|
||||||
39
base/src/test/user_model/test_rename_sheet.rs
Normal file
39
base/src/test/user_model/test_rename_sheet.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::UserModel;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_rename() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model.rename_sheet(0, "NewSheet").unwrap();
|
||||||
|
assert_eq!(model.get_sheets_info()[0].name, "NewSheet");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn undo_redo() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model.rename_sheet(0, "NewSheet").unwrap();
|
||||||
|
model.undo().unwrap();
|
||||||
|
assert_eq!(model.get_sheets_info()[0].name, "Sheet1");
|
||||||
|
model.redo().unwrap();
|
||||||
|
assert_eq!(model.get_sheets_info()[0].name, "NewSheet");
|
||||||
|
|
||||||
|
let send_queue = model.flush_send_queue();
|
||||||
|
|
||||||
|
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
assert_eq!(model.get_sheets_info()[0].name, "NewSheet");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn errors() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
model.rename_sheet(0, ""),
|
||||||
|
Err("Invalid name for a sheet: ''.".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
model.rename_sheet(1, "Hello"),
|
||||||
|
Err("Invalid sheet index".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
156
base/src/test/user_model/test_row_column.rs
Normal file
156
base/src/test/user_model/test_row_column.rs
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
constants::{DEFAULT_COLUMN_WIDTH, DEFAULT_ROW_HEIGHT, LAST_COLUMN},
|
||||||
|
test::util::new_empty_model,
|
||||||
|
UserModel,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_insert_row() {
|
||||||
|
let model = new_empty_model();
|
||||||
|
let mut model = UserModel::from_model(model);
|
||||||
|
let (sheet, column) = (0, 5);
|
||||||
|
for row in 1..5 {
|
||||||
|
assert!(model.set_user_input(sheet, row, column, "123").is_ok());
|
||||||
|
}
|
||||||
|
assert!(model.insert_row(sheet, 3).is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(sheet, 3, column).unwrap(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(model.undo().is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(sheet, 3, column).unwrap(),
|
||||||
|
"123"
|
||||||
|
);
|
||||||
|
assert!(model.redo().is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(sheet, 3, column).unwrap(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_insert_column() {
|
||||||
|
let model = new_empty_model();
|
||||||
|
let mut model = UserModel::from_model(model);
|
||||||
|
let (sheet, row) = (0, 5);
|
||||||
|
for column in 1..5 {
|
||||||
|
assert!(model.set_user_input(sheet, row, column, "123").is_ok());
|
||||||
|
}
|
||||||
|
assert!(model.insert_column(sheet, 3).is_ok());
|
||||||
|
assert_eq!(model.get_formatted_cell_value(sheet, row, 3).unwrap(), "");
|
||||||
|
|
||||||
|
assert!(model.undo().is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(sheet, row, 3).unwrap(),
|
||||||
|
"123"
|
||||||
|
);
|
||||||
|
assert!(model.redo().is_ok());
|
||||||
|
assert_eq!(model.get_formatted_cell_value(sheet, row, 3).unwrap(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_delete_column() {
|
||||||
|
let model = new_empty_model();
|
||||||
|
let mut model = UserModel::from_model(model);
|
||||||
|
model.set_user_input(0, 1, 5, "3").unwrap();
|
||||||
|
model.set_user_input(0, 2, 5, "=E1*2").unwrap();
|
||||||
|
model
|
||||||
|
.set_column_width(0, 5, DEFAULT_COLUMN_WIDTH * 3.0)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
model.delete_column(0, 5).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 2, 5), Ok("".to_string()));
|
||||||
|
assert_eq!(model.get_column_width(0, 5), Ok(DEFAULT_COLUMN_WIDTH));
|
||||||
|
|
||||||
|
model.undo().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 2, 5), Ok("6".to_string()));
|
||||||
|
assert_eq!(model.get_column_width(0, 5), Ok(DEFAULT_COLUMN_WIDTH * 3.0));
|
||||||
|
|
||||||
|
let send_queue = model.flush_send_queue();
|
||||||
|
|
||||||
|
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_formatted_cell_value(0, 2, 5),
|
||||||
|
Ok("6".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_column_width(0, 5),
|
||||||
|
Ok(DEFAULT_COLUMN_WIDTH * 3.0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delete_column_errors() {
|
||||||
|
let model = new_empty_model();
|
||||||
|
let mut model = UserModel::from_model(model);
|
||||||
|
assert_eq!(
|
||||||
|
model.delete_column(1, 1),
|
||||||
|
Err("Invalid sheet index".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.delete_column(0, 0),
|
||||||
|
Err("Column number '0' is not valid.".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
model.delete_column(0, LAST_COLUMN + 1),
|
||||||
|
Err("Column number '16385' is not valid.".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(model.delete_column(0, LAST_COLUMN), Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_delete_row() {
|
||||||
|
let model = new_empty_model();
|
||||||
|
let mut model = UserModel::from_model(model);
|
||||||
|
model.set_user_input(0, 15, 4, "3").unwrap();
|
||||||
|
model.set_user_input(0, 15, 6, "=D15*2").unwrap();
|
||||||
|
|
||||||
|
model
|
||||||
|
.set_row_height(0, 15, DEFAULT_ROW_HEIGHT * 3.0)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
model.delete_row(0, 15).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 15, 6), Ok("".to_string()));
|
||||||
|
assert_eq!(model.get_row_height(0, 15), Ok(DEFAULT_ROW_HEIGHT));
|
||||||
|
|
||||||
|
model.undo().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 15, 6),
|
||||||
|
Ok("6".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(model.get_row_height(0, 15), Ok(DEFAULT_ROW_HEIGHT * 3.0));
|
||||||
|
|
||||||
|
let send_queue = model.flush_send_queue();
|
||||||
|
|
||||||
|
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model2.get_formatted_cell_value(0, 15, 6),
|
||||||
|
Ok("6".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(model2.get_row_height(0, 15), Ok(DEFAULT_ROW_HEIGHT * 3.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_delete_row_no_style() {
|
||||||
|
let model = new_empty_model();
|
||||||
|
let mut model = UserModel::from_model(model);
|
||||||
|
model.set_user_input(0, 15, 4, "3").unwrap();
|
||||||
|
model.set_user_input(0, 15, 6, "=D15*2").unwrap();
|
||||||
|
model.delete_row(0, 15).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 15, 6), Ok("".to_string()));
|
||||||
|
}
|
||||||
711
base/src/test/user_model/test_styles.rs
Normal file
711
base/src/test/user_model/test_styles.rs
Normal file
@@ -0,0 +1,711 @@
|
|||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
expressions::types::Area,
|
||||||
|
types::{Alignment, BorderItem, BorderStyle, HorizontalAlignment, VerticalAlignment},
|
||||||
|
UserModel,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_fonts() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert!(!style.font.i);
|
||||||
|
assert!(!style.font.b);
|
||||||
|
assert!(!style.font.u);
|
||||||
|
assert!(!style.font.strike);
|
||||||
|
assert_eq!(style.font.color, Some("#000000".to_owned()));
|
||||||
|
|
||||||
|
// bold
|
||||||
|
model.update_range_style(&range, "font.b", "true").unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert!(style.font.b);
|
||||||
|
|
||||||
|
// italics
|
||||||
|
model.update_range_style(&range, "font.i", "true").unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert!(style.font.i);
|
||||||
|
|
||||||
|
// underline
|
||||||
|
model.update_range_style(&range, "font.u", "true").unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert!(style.font.u);
|
||||||
|
|
||||||
|
// strike
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "font.strike", "true")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert!(style.font.strike);
|
||||||
|
|
||||||
|
// color
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "font.color", "#F1F1F1")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(style.font.color, Some("#F1F1F1".to_owned()));
|
||||||
|
|
||||||
|
while model.can_undo() {
|
||||||
|
model.undo().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert!(!style.font.i);
|
||||||
|
assert!(!style.font.b);
|
||||||
|
assert!(!style.font.u);
|
||||||
|
assert!(!style.font.strike);
|
||||||
|
assert_eq!(style.font.color, Some("#000000".to_owned()));
|
||||||
|
|
||||||
|
while model.can_redo() {
|
||||||
|
model.redo().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert!(style.font.i);
|
||||||
|
assert!(style.font.b);
|
||||||
|
assert!(style.font.u);
|
||||||
|
assert!(style.font.strike);
|
||||||
|
assert_eq!(style.font.color, Some("#F1F1F1".to_owned()));
|
||||||
|
|
||||||
|
let send_queue = model.flush_send_queue();
|
||||||
|
|
||||||
|
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
|
||||||
|
let style = model2.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert!(style.font.i);
|
||||||
|
assert!(style.font.b);
|
||||||
|
assert!(style.font.u);
|
||||||
|
assert!(style.font.strike);
|
||||||
|
assert_eq!(style.font.color, Some("#F1F1F1".to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn font_errors() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "font.b", "True"),
|
||||||
|
Err("Invalid value for boolean: 'True'.".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "font.i", "FALSE"),
|
||||||
|
Err("Invalid value for boolean: 'FALSE'.".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "font.bold", "true"),
|
||||||
|
Err("Invalid style path: 'font.bold'.".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "font.strike", ""),
|
||||||
|
Err("Invalid value for boolean: ''.".to_string())
|
||||||
|
);
|
||||||
|
// There is no cast for booleans
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "font.b", "1"),
|
||||||
|
Err("Invalid value for boolean: '1'.".to_string())
|
||||||
|
);
|
||||||
|
// colors don't work by name
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "font.color", "blue"),
|
||||||
|
Err("Invalid color: 'blue'.".to_string())
|
||||||
|
);
|
||||||
|
// No short form
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "font.color", "#FFF"),
|
||||||
|
Err("Invalid color: '#FFF'.".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_fill() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(style.fill.bg_color, None);
|
||||||
|
|
||||||
|
// bg_color
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "fill.bg_color", "#F2F2F2")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(style.fill.bg_color, Some("#F2F2F2".to_owned()));
|
||||||
|
|
||||||
|
let send_queue = model.flush_send_queue();
|
||||||
|
|
||||||
|
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
|
||||||
|
let style = model2.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(style.fill.bg_color, Some("#F2F2F2".to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fill_errors() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
assert!(model
|
||||||
|
.update_range_style(&range, "fill.bg_color", "#FFF")
|
||||||
|
.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_format() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(style.num_fmt, "general");
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "num_fmt", "$#,##0.0000")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(style.num_fmt, "$#,##0.0000");
|
||||||
|
|
||||||
|
model.undo().unwrap();
|
||||||
|
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(style.num_fmt, "general");
|
||||||
|
|
||||||
|
model.redo().unwrap();
|
||||||
|
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(style.num_fmt, "$#,##0.0000");
|
||||||
|
|
||||||
|
let send_queue = model.flush_send_queue();
|
||||||
|
|
||||||
|
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
|
||||||
|
let style = model2.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(style.num_fmt, "$#,##0.0000");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_borders() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "border.left", "thin,#F1F1F1")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.left,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Thin,
|
||||||
|
color: Some("#F1F1F1".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "border.left", "thin,")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.left,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Thin,
|
||||||
|
color: None,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "border.right", "dotted,#F1F1F2")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.right,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Dotted,
|
||||||
|
color: Some("#F1F1F2".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "border.top", "double,#F1F1F3")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.top,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Double,
|
||||||
|
color: Some("#F1F1F3".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "border.bottom", "medium,#F1F1F4")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.bottom,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Medium,
|
||||||
|
color: Some("#F1F1F4".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
while model.can_undo() {
|
||||||
|
model.undo().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(style.border.left, None);
|
||||||
|
assert_eq!(style.border.top, None);
|
||||||
|
assert_eq!(style.border.right, None);
|
||||||
|
assert_eq!(style.border.bottom, None);
|
||||||
|
|
||||||
|
while model.can_redo() {
|
||||||
|
model.redo().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.left,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Thin,
|
||||||
|
color: None,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
style.border.right,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Dotted,
|
||||||
|
color: Some("#F1F1F2".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
style.border.top,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Double,
|
||||||
|
color: Some("#F1F1F3".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
style.border.bottom,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Medium,
|
||||||
|
color: Some("#F1F1F4".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let send_queue = model.flush_send_queue();
|
||||||
|
|
||||||
|
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
model2.apply_external_diffs(&send_queue).unwrap();
|
||||||
|
|
||||||
|
let style = model2.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.left,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Thin,
|
||||||
|
color: None,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
style.border.right,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Dotted,
|
||||||
|
color: Some("#F1F1F2".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
style.border.top,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Double,
|
||||||
|
color: Some("#F1F1F3".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
style.border.bottom,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Medium,
|
||||||
|
color: Some("#F1F1F4".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_alignment() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let alignment = model.get_cell_style(0, 1, 1).unwrap().alignment;
|
||||||
|
assert_eq!(alignment, None);
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "alignment.horizontal", "center")
|
||||||
|
.unwrap();
|
||||||
|
let alignment = model.get_cell_style(0, 1, 1).unwrap().alignment;
|
||||||
|
assert_eq!(
|
||||||
|
alignment,
|
||||||
|
Some(Alignment {
|
||||||
|
horizontal: HorizontalAlignment::Center,
|
||||||
|
vertical: VerticalAlignment::Bottom,
|
||||||
|
wrap_text: false
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "alignment.horizontal", "centerContinuous")
|
||||||
|
.unwrap();
|
||||||
|
let alignment = model.get_cell_style(0, 1, 1).unwrap().alignment;
|
||||||
|
assert_eq!(
|
||||||
|
alignment,
|
||||||
|
Some(Alignment {
|
||||||
|
horizontal: HorizontalAlignment::CenterContinuous,
|
||||||
|
vertical: VerticalAlignment::Bottom,
|
||||||
|
wrap_text: false
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 2,
|
||||||
|
column: 2,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "alignment.vertical", "distributed")
|
||||||
|
.unwrap();
|
||||||
|
let alignment = model.get_cell_style(0, 2, 2).unwrap().alignment;
|
||||||
|
assert_eq!(
|
||||||
|
alignment,
|
||||||
|
Some(Alignment {
|
||||||
|
horizontal: HorizontalAlignment::General,
|
||||||
|
vertical: VerticalAlignment::Distributed,
|
||||||
|
wrap_text: false
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "alignment.vertical", "justify")
|
||||||
|
.unwrap();
|
||||||
|
let alignment = model.get_cell_style(0, 2, 2).unwrap().alignment;
|
||||||
|
assert_eq!(
|
||||||
|
alignment,
|
||||||
|
Some(Alignment {
|
||||||
|
horizontal: HorizontalAlignment::General,
|
||||||
|
vertical: VerticalAlignment::Justify,
|
||||||
|
wrap_text: false
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
model.update_range_style(&range, "alignment", "").unwrap();
|
||||||
|
let alignment = model.get_cell_style(0, 2, 2).unwrap().alignment;
|
||||||
|
assert_eq!(alignment, None);
|
||||||
|
|
||||||
|
model.undo().unwrap();
|
||||||
|
|
||||||
|
let alignment = model.get_cell_style(0, 2, 2).unwrap().alignment;
|
||||||
|
assert_eq!(
|
||||||
|
alignment,
|
||||||
|
Some(Alignment {
|
||||||
|
horizontal: HorizontalAlignment::General,
|
||||||
|
vertical: VerticalAlignment::Justify,
|
||||||
|
wrap_text: false
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alignment_errors() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "alignment", "some"),
|
||||||
|
Err("Alignment must be empty, but found: 'some'.".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "alignment.vertical", "justified"),
|
||||||
|
Err("Invalid value for vertical alignment: 'justified'.".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "alignment.horizontal", "unjustified"),
|
||||||
|
Err("Invalid value for horizontal alignment: 'unjustified'.".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "alignment.vertical", "justify")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Also fail if there is an alignment
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "alignment", "some"),
|
||||||
|
Err("Alignment must be empty, but found: 'some'.".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "alignment.vertical", "justified"),
|
||||||
|
Err("Invalid value for vertical alignment: 'justified'.".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "alignment.horizontal", "unjustified"),
|
||||||
|
Err("Invalid value for horizontal alignment: 'unjustified'.".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_wrap_text() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "alignment.wrap_text", "T"),
|
||||||
|
Err("Invalid value for boolean: 'T'.".to_string())
|
||||||
|
);
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "alignment.wrap_text", "true")
|
||||||
|
.unwrap();
|
||||||
|
let alignment = model.get_cell_style(0, 1, 1).unwrap().alignment;
|
||||||
|
assert_eq!(
|
||||||
|
alignment,
|
||||||
|
Some(Alignment {
|
||||||
|
horizontal: HorizontalAlignment::General,
|
||||||
|
vertical: VerticalAlignment::Bottom,
|
||||||
|
wrap_text: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
model.undo().unwrap();
|
||||||
|
let alignment = model.get_cell_style(0, 1, 1).unwrap().alignment;
|
||||||
|
assert_eq!(alignment, None);
|
||||||
|
|
||||||
|
model.redo().unwrap();
|
||||||
|
|
||||||
|
let alignment = model.get_cell_style(0, 1, 1).unwrap().alignment;
|
||||||
|
assert_eq!(
|
||||||
|
alignment,
|
||||||
|
Some(Alignment {
|
||||||
|
horizontal: HorizontalAlignment::General,
|
||||||
|
vertical: VerticalAlignment::Bottom,
|
||||||
|
wrap_text: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "alignment.wrap_text", "True"),
|
||||||
|
Err("Invalid value for boolean: 'True'.".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn more_basic_borders() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "border.left", "thick,#F1F1F1")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.left,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::Thick,
|
||||||
|
color: Some("#F1F1F1".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "border.left", "slantDashDot,#F1F1F1")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.left,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::SlantDashDot,
|
||||||
|
color: Some("#F1F1F1".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "border.left", "mediumDashDot,#F1F1F1")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.left,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::MediumDashDot,
|
||||||
|
color: Some("#F1F1F1".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "border.left", "mediumDashDotDot,#F1F1F1")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.left,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::MediumDashDotDot,
|
||||||
|
color: Some("#F1F1F1".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "border.left", "mediumDashed,#F1F1F1")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.left,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::MediumDashed,
|
||||||
|
color: Some("#F1F1F1".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn border_errors() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "border.lef", "thick,#F1F1F1"),
|
||||||
|
Err("Invalid style path: 'border.lef'.".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "border.left", "thic,#F1F1F1"),
|
||||||
|
Err("Invalid border style: 'thic'.".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "border.left", "thick,#F1F1F"),
|
||||||
|
Err("Invalid color: '#F1F1F'.".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "border.left", " "),
|
||||||
|
Err("Invalid border value: ' '.".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
model.update_range_style(&range, "border.left", "thick,#F1F1F1,thin"),
|
||||||
|
Err("Invalid border value: 'thick,#F1F1F1,thin'.".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_removes_border() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
model
|
||||||
|
.update_range_style(&range, "border.left", "mediumDashDotDot,#F1F1F1")
|
||||||
|
.unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
style.border.left,
|
||||||
|
Some(BorderItem {
|
||||||
|
style: BorderStyle::MediumDashDotDot,
|
||||||
|
color: Some("#F1F1F1".to_owned()),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
model.update_range_style(&range, "border.left", "").unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert_eq!(style.border.left, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn false_removes_value() {
|
||||||
|
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||||
|
let range = Area {
|
||||||
|
sheet: 0,
|
||||||
|
row: 1,
|
||||||
|
column: 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// bold
|
||||||
|
model.update_range_style(&range, "font.b", "true").unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert!(style.font.b);
|
||||||
|
|
||||||
|
model.update_range_style(&range, "font.b", "false").unwrap();
|
||||||
|
let style = model.get_cell_style(0, 1, 1).unwrap();
|
||||||
|
assert!(!style.font.b);
|
||||||
|
}
|
||||||
66
base/src/test/user_model/test_undo_redo.rs
Normal file
66
base/src/test/user_model/test_undo_redo.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::{test::util::new_empty_model, UserModel};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_undo_redo() {
|
||||||
|
let model = new_empty_model();
|
||||||
|
let mut model = UserModel::from_model(model);
|
||||||
|
// at the beginning I cannot undo or redo
|
||||||
|
assert!(!model.can_undo());
|
||||||
|
assert!(!model.can_redo());
|
||||||
|
assert!(model.set_user_input(0, 1, 1, "=1+2").is_ok());
|
||||||
|
|
||||||
|
// Once I enter a value I can undo but not redo
|
||||||
|
assert!(model.can_undo());
|
||||||
|
assert!(!model.can_redo());
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("3".to_string()));
|
||||||
|
|
||||||
|
// If I undo, I can't undo anymore, but I can redo
|
||||||
|
assert!(model.undo().is_ok());
|
||||||
|
assert!(!model.can_undo());
|
||||||
|
assert!(model.can_redo());
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
|
||||||
|
|
||||||
|
// If I redo, I have the old value and formula
|
||||||
|
assert!(model.redo().is_ok());
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("3".to_string()));
|
||||||
|
assert_eq!(model.get_cell_content(0, 1, 1), Ok("=1+2".to_string()));
|
||||||
|
assert!(model.can_undo());
|
||||||
|
assert!(!model.can_redo());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn undo_redo_respect_styles() {
|
||||||
|
let model = new_empty_model();
|
||||||
|
let mut model = UserModel::from_model(model);
|
||||||
|
assert!(model.set_user_input(0, 1, 1, "100").is_ok());
|
||||||
|
assert!(model.set_user_input(0, 1, 1, "125$").is_ok());
|
||||||
|
// The content of the cell is just the number 125
|
||||||
|
assert_eq!(model.get_cell_content(0, 1, 1), Ok("125".to_string()));
|
||||||
|
assert!(model.undo().is_ok());
|
||||||
|
// The cell has no currency number formatting
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 1, 1),
|
||||||
|
Ok("100".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(model.get_cell_content(0, 1, 1), Ok("100".to_string()));
|
||||||
|
assert!(model.redo().is_ok());
|
||||||
|
// The cell has the number 125 formatted as '125$'
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 1, 1),
|
||||||
|
Ok("125$".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(model.get_cell_content(0, 1, 1), Ok("125".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_undo_can_redo() {
|
||||||
|
let model = new_empty_model();
|
||||||
|
let mut model = UserModel::from_model(model);
|
||||||
|
assert!(!model.can_undo());
|
||||||
|
assert!(!model.can_redo());
|
||||||
|
|
||||||
|
assert!(model.undo().is_ok());
|
||||||
|
assert!(model.redo().is_ok());
|
||||||
|
}
|
||||||
@@ -32,11 +32,11 @@ impl Model {
|
|||||||
let cell_reference = self._parse_reference(cell);
|
let cell_reference = self._parse_reference(cell);
|
||||||
let column = cell_reference.column;
|
let column = cell_reference.column;
|
||||||
let row = cell_reference.row;
|
let row = cell_reference.row;
|
||||||
self.cell_formula(cell_reference.sheet, row, column)
|
self.get_cell_formula(cell_reference.sheet, row, column)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
pub fn _get_text_at(&self, sheet: u32, row: i32, column: i32) -> String {
|
pub fn _get_text_at(&self, sheet: u32, row: i32, column: i32) -> String {
|
||||||
self.formatted_cell_value(sheet, row, column).unwrap()
|
self.get_formatted_cell_value(sheet, row, column).unwrap()
|
||||||
}
|
}
|
||||||
pub fn _get_text(&self, cell: &str) -> String {
|
pub fn _get_text(&self, cell: &str) -> String {
|
||||||
let CellReferenceIndex { sheet, row, column } = self._parse_reference(cell);
|
let CellReferenceIndex { sheet, row, column } = self._parse_reference(cell);
|
||||||
|
|||||||
1183
base/src/user_model.rs
Normal file
1183
base/src/user_model.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -27,16 +27,4 @@ impl Workbook {
|
|||||||
.get_mut(worksheet_index as usize)
|
.get_mut(worksheet_index as usize)
|
||||||
.ok_or_else(|| "Invalid sheet index".to_string())
|
.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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ impl Worksheet {
|
|||||||
self.sheet_data.get_mut(&row)?.get_mut(&column)
|
self.sheet_data.get_mut(&row)?.get_mut(&column)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_cell(&mut self, row: i32, column: i32, new_cell: Cell) {
|
pub(crate) fn update_cell(&mut self, row: i32, column: i32, new_cell: Cell) {
|
||||||
match self.sheet_data.get_mut(&row) {
|
match self.sheet_data.get_mut(&row) {
|
||||||
Some(column_data) => match column_data.get(&column) {
|
Some(column_data) => match column_data.get(&column) {
|
||||||
Some(_cell) => {
|
Some(_cell) => {
|
||||||
@@ -68,9 +68,8 @@ impl Worksheet {
|
|||||||
if row.r == row_index {
|
if row.r == row_index {
|
||||||
if row.custom_format {
|
if row.custom_format {
|
||||||
return row.s;
|
return row.s;
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let cols = &self.cols;
|
let cols = &self.cols;
|
||||||
@@ -106,64 +105,8 @@ impl Worksheet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_column_style(&mut self, column: i32, style_index: i32) -> Result<(), String> {
|
pub fn set_column_style(&mut self, column: i32, style_index: i32) -> Result<(), String> {
|
||||||
let cols = &mut self.cols;
|
let width = constants::DEFAULT_COLUMN_WIDTH / constants::COLUMN_WIDTH_FACTOR;
|
||||||
let col = Col {
|
self.set_column_width_and_style(column, width, Some(style_index))
|
||||||
min: column,
|
|
||||||
max: column,
|
|
||||||
width: constants::DEFAULT_COLUMN_WIDTH / constants::COLUMN_WIDTH_FACTOR,
|
|
||||||
custom_width: true,
|
|
||||||
style: Some(style_index),
|
|
||||||
};
|
|
||||||
let mut index = 0;
|
|
||||||
let mut split = false;
|
|
||||||
for c in cols.iter_mut() {
|
|
||||||
let min = c.min;
|
|
||||||
let max = c.max;
|
|
||||||
if min <= column && column <= max {
|
|
||||||
if min == column && max == column {
|
|
||||||
c.style = Some(style_index);
|
|
||||||
return Ok(());
|
|
||||||
} else {
|
|
||||||
// We need to split the result
|
|
||||||
split = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if column < min {
|
|
||||||
// We passed, we should insert at index
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
if split {
|
|
||||||
let min = cols[index].min;
|
|
||||||
let max = cols[index].max;
|
|
||||||
let pre = Col {
|
|
||||||
min,
|
|
||||||
max: column - 1,
|
|
||||||
width: cols[index].width,
|
|
||||||
custom_width: cols[index].custom_width,
|
|
||||||
style: cols[index].style,
|
|
||||||
};
|
|
||||||
let post = Col {
|
|
||||||
min: column + 1,
|
|
||||||
max,
|
|
||||||
width: cols[index].width,
|
|
||||||
custom_width: cols[index].custom_width,
|
|
||||||
style: cols[index].style,
|
|
||||||
};
|
|
||||||
cols.remove(index);
|
|
||||||
if column != max {
|
|
||||||
cols.insert(index, post);
|
|
||||||
}
|
|
||||||
cols.insert(index, col);
|
|
||||||
if column != min {
|
|
||||||
cols.insert(index, pre);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cols.insert(index, col);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_row_style(&mut self, row: i32, style_index: i32) -> Result<(), String> {
|
pub fn set_row_style(&mut self, row: i32, style_index: i32) -> Result<(), String> {
|
||||||
@@ -191,7 +134,7 @@ impl Worksheet {
|
|||||||
cell.set_style(style_index);
|
cell.set_style(style_index);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
self.set_cell_empty_with_style(row, column, style_index);
|
self.cell_clear_contents_with_style(row, column, style_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,13 +166,13 @@ impl Worksheet {
|
|||||||
self.update_cell(row, column, cell);
|
self.update_cell(row, column, cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cell_empty(&mut self, row: i32, column: i32) {
|
pub fn cell_clear_contents(&mut self, row: i32, column: i32) {
|
||||||
let s = self.get_style(row, column);
|
let s = self.get_style(row, column);
|
||||||
let cell = Cell::EmptyCell { s };
|
let cell = Cell::EmptyCell { s };
|
||||||
self.update_cell(row, column, cell);
|
self.update_cell(row, column, cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cell_empty_with_style(&mut self, row: i32, column: i32, style: i32) {
|
pub fn cell_clear_contents_with_style(&mut self, row: i32, column: i32, style: i32) {
|
||||||
let cell = Cell::EmptyCell { s: style };
|
let cell = Cell::EmptyCell { s: style };
|
||||||
self.update_cell(row, column, cell);
|
self.update_cell(row, column, cell);
|
||||||
}
|
}
|
||||||
@@ -237,7 +180,8 @@ impl Worksheet {
|
|||||||
pub fn set_frozen_rows(&mut self, frozen_rows: i32) -> Result<(), String> {
|
pub fn set_frozen_rows(&mut self, frozen_rows: i32) -> Result<(), String> {
|
||||||
if frozen_rows < 0 {
|
if frozen_rows < 0 {
|
||||||
return Err("Frozen rows cannot be negative".to_string());
|
return Err("Frozen rows cannot be negative".to_string());
|
||||||
} else if frozen_rows >= constants::LAST_ROW {
|
}
|
||||||
|
if frozen_rows >= constants::LAST_ROW {
|
||||||
return Err("Too many rows".to_string());
|
return Err("Too many rows".to_string());
|
||||||
}
|
}
|
||||||
self.frozen_rows = frozen_rows;
|
self.frozen_rows = frozen_rows;
|
||||||
@@ -247,7 +191,8 @@ impl Worksheet {
|
|||||||
pub fn set_frozen_columns(&mut self, frozen_columns: i32) -> Result<(), String> {
|
pub fn set_frozen_columns(&mut self, frozen_columns: i32) -> Result<(), String> {
|
||||||
if frozen_columns < 0 {
|
if frozen_columns < 0 {
|
||||||
return Err("Frozen columns cannot be negative".to_string());
|
return Err("Frozen columns cannot be negative".to_string());
|
||||||
} else if frozen_columns >= constants::LAST_COLUMN {
|
}
|
||||||
|
if frozen_columns >= constants::LAST_COLUMN {
|
||||||
return Err("Too many columns".to_string());
|
return Err("Too many columns".to_string());
|
||||||
}
|
}
|
||||||
self.frozen_columns = frozen_columns;
|
self.frozen_columns = frozen_columns;
|
||||||
@@ -281,11 +226,21 @@ impl Worksheet {
|
|||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the width of a column.
|
/// Changes the width of a column.
|
||||||
/// * If the column does not a have a width we simply add it
|
/// * If the column does not a have a width we simply add it
|
||||||
/// * If it has, it might be part of a range and we ned to split the range.
|
/// * If it has, it might be part of a range and we ned to split the range.
|
||||||
/// Fails if column index is outside allowed range.
|
/// Fails if column index is outside allowed range.
|
||||||
pub fn set_column_width(&mut self, column: i32, width: f64) -> Result<(), String> {
|
pub fn set_column_width(&mut self, column: i32, width: f64) -> Result<(), String> {
|
||||||
|
self.set_column_width_and_style(column, width, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_column_width_and_style(
|
||||||
|
&mut self,
|
||||||
|
column: i32,
|
||||||
|
width: f64,
|
||||||
|
style: Option<i32>,
|
||||||
|
) -> Result<(), String> {
|
||||||
if !is_valid_column_number(column) {
|
if !is_valid_column_number(column) {
|
||||||
return Err(format!("Column number '{column}' is not valid."));
|
return Err(format!("Column number '{column}' is not valid."));
|
||||||
}
|
}
|
||||||
@@ -295,7 +250,7 @@ impl Worksheet {
|
|||||||
max: column,
|
max: column,
|
||||||
width: width / constants::COLUMN_WIDTH_FACTOR,
|
width: width / constants::COLUMN_WIDTH_FACTOR,
|
||||||
custom_width: true,
|
custom_width: true,
|
||||||
style: None,
|
style,
|
||||||
};
|
};
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
let mut split = false;
|
let mut split = false;
|
||||||
@@ -306,12 +261,10 @@ impl Worksheet {
|
|||||||
if min == column && max == column {
|
if min == column && max == column {
|
||||||
c.width = width / constants::COLUMN_WIDTH_FACTOR;
|
c.width = width / constants::COLUMN_WIDTH_FACTOR;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
}
|
||||||
// We need to split the result
|
|
||||||
split = true;
|
split = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if column < min {
|
if column < min {
|
||||||
// We passed, we should insert at index
|
// We passed, we should insert at index
|
||||||
break;
|
break;
|
||||||
@@ -351,7 +304,7 @@ impl Worksheet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the width of a column in pixels
|
/// Return the width of a column in pixels
|
||||||
pub fn column_width(&self, column: i32) -> Result<f64, String> {
|
pub fn get_column_width(&self, column: i32) -> Result<f64, String> {
|
||||||
if !is_valid_column_number(column) {
|
if !is_valid_column_number(column) {
|
||||||
return Err(format!("Column number '{column}' is not valid."));
|
return Err(format!("Column number '{column}' is not valid."));
|
||||||
}
|
}
|
||||||
@@ -363,9 +316,8 @@ impl Worksheet {
|
|||||||
if column >= min && column <= max {
|
if column >= min && column <= max {
|
||||||
if col.custom_width {
|
if col.custom_width {
|
||||||
return Ok(col.width * constants::COLUMN_WIDTH_FACTOR);
|
return Ok(col.width * constants::COLUMN_WIDTH_FACTOR);
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(constants::DEFAULT_COLUMN_WIDTH)
|
Ok(constants::DEFAULT_COLUMN_WIDTH)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use ironcalc::{
|
use ironcalc::{
|
||||||
base::{expressions::utils::number_to_column, model::Model},
|
base::{expressions::utils::number_to_column, Model},
|
||||||
export::save_to_xlsx,
|
export::save_to_xlsx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use ironcalc::{base::model::Model, export::save_to_xlsx};
|
use ironcalc::{base::Model, export::save_to_xlsx};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut model = Model::new_empty("hello_styles", "en", "UTC")?;
|
let mut model = Model::new_empty("hello_styles", "en", "UTC")?;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use ironcalc::{base::model::Model, export::save_to_xlsx};
|
use ironcalc::{base::Model, export::save_to_xlsx};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut model = Model::new_empty("widths-and-heights", "en", "UTC")?;
|
let mut model = Model::new_empty("widths-and-heights", "en", "UTC")?;
|
||||||
@@ -6,7 +6,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let (sheet, row, column) = (0, 5, 3);
|
let (sheet, row, column) = (0, 5, 3);
|
||||||
// Make the first column 4 times as width
|
// Make the first column 4 times as width
|
||||||
let worksheet = model.workbook.worksheet_mut(sheet)?;
|
let worksheet = model.workbook.worksheet_mut(sheet)?;
|
||||||
let column_width = worksheet.column_width(column)? * 4.0;
|
let column_width = worksheet.get_column_width(column)? * 4.0;
|
||||||
worksheet.set_column_width(column, column_width)?;
|
worksheet.set_column_width(column, column_width)?;
|
||||||
|
|
||||||
// and the first row twice as high.
|
// and the first row twice as high.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use ironcalc_base::model::Model;
|
use ironcalc_base::Model;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<_> = std::env::args().collect();
|
let args: Vec<_> = std::env::args().collect();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::path::Path;
|
|||||||
|
|
||||||
use ironcalc_base::cell::CellValue;
|
use ironcalc_base::cell::CellValue;
|
||||||
use ironcalc_base::types::*;
|
use ironcalc_base::types::*;
|
||||||
use ironcalc_base::{expressions::utils::number_to_column, model::Model};
|
use ironcalc_base::{expressions::utils::number_to_column, Model};
|
||||||
|
|
||||||
use crate::export::save_to_xlsx;
|
use crate::export::save_to_xlsx;
|
||||||
use crate::import::load_model_from_xlsx;
|
use crate::import::load_model_from_xlsx;
|
||||||
@@ -208,7 +208,7 @@ pub fn test_load_and_saving(file_path: &str, temp_dir_name: &Path) -> Result<(),
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::compare::compare;
|
use crate::compare::compare;
|
||||||
use ironcalc_base::model::Model;
|
use ironcalc_base::Model;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compare_different_sheets() {
|
fn compare_different_sheets() {
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use ironcalc_base::expressions::utils::number_to_column;
|
use ironcalc_base::expressions::utils::number_to_column;
|
||||||
use ironcalc_base::model::{get_milliseconds_since_epoch, Model};
|
|
||||||
use ironcalc_base::types::Workbook;
|
use ironcalc_base::types::Workbook;
|
||||||
|
use ironcalc_base::{get_milliseconds_since_epoch, Model};
|
||||||
|
|
||||||
use self::xml_constants::XML_DECLARATION;
|
use self::xml_constants::XML_DECLARATION;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use ironcalc_base::model::Model;
|
use ironcalc_base::Model;
|
||||||
|
|
||||||
use crate::error::XlsxError;
|
use crate::error::XlsxError;
|
||||||
use crate::{export::save_to_xlsx, import::load_model_from_xlsx};
|
use crate::{export::save_to_xlsx, import::load_model_from_xlsx};
|
||||||
@@ -31,13 +31,22 @@ fn test_values() {
|
|||||||
save_to_xlsx(&model, temp_file_name).unwrap();
|
save_to_xlsx(&model, temp_file_name).unwrap();
|
||||||
|
|
||||||
let model = load_model_from_xlsx(temp_file_name, "en", "UTC").unwrap();
|
let model = load_model_from_xlsx(temp_file_name, "en", "UTC").unwrap();
|
||||||
assert_eq!(model.formatted_cell_value(0, 1, 1).unwrap(), "123.456");
|
assert_eq!(model.get_formatted_cell_value(0, 1, 1).unwrap(), "123.456");
|
||||||
assert_eq!(model.formatted_cell_value(0, 2, 1).unwrap(), "Hello world!");
|
assert_eq!(
|
||||||
assert_eq!(model.formatted_cell_value(0, 3, 1).unwrap(), "Hello world!");
|
model.get_formatted_cell_value(0, 2, 1).unwrap(),
|
||||||
assert_eq!(model.formatted_cell_value(0, 4, 1).unwrap(), "你好世界!");
|
"Hello world!"
|
||||||
assert_eq!(model.formatted_cell_value(0, 5, 1).unwrap(), "TRUE");
|
);
|
||||||
assert_eq!(model.formatted_cell_value(0, 6, 1).unwrap(), "FALSE");
|
assert_eq!(
|
||||||
assert_eq!(model.formatted_cell_value(0, 7, 1).unwrap(), "#VALUE!");
|
model.get_formatted_cell_value(0, 3, 1).unwrap(),
|
||||||
|
"Hello world!"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
model.get_formatted_cell_value(0, 4, 1).unwrap(),
|
||||||
|
"你好世界!"
|
||||||
|
);
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 5, 1).unwrap(), "TRUE");
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 6, 1).unwrap(), "FALSE");
|
||||||
|
assert_eq!(model.get_formatted_cell_value(0, 7, 1).unwrap(), "#VALUE!");
|
||||||
|
|
||||||
fs::remove_file(temp_file_name).unwrap();
|
fs::remove_file(temp_file_name).unwrap();
|
||||||
}
|
}
|
||||||
@@ -59,10 +68,10 @@ fn test_formulas() {
|
|||||||
save_to_xlsx(&model, temp_file_name).unwrap();
|
save_to_xlsx(&model, temp_file_name).unwrap();
|
||||||
|
|
||||||
let model = load_model_from_xlsx(temp_file_name, "en", "UTC").unwrap();
|
let model = load_model_from_xlsx(temp_file_name, "en", "UTC").unwrap();
|
||||||
assert_eq!(model.formatted_cell_value(0, 1, 2).unwrap(), "11");
|
assert_eq!(model.get_formatted_cell_value(0, 1, 2).unwrap(), "11");
|
||||||
assert_eq!(model.formatted_cell_value(0, 2, 2).unwrap(), "13");
|
assert_eq!(model.get_formatted_cell_value(0, 2, 2).unwrap(), "13");
|
||||||
assert_eq!(model.formatted_cell_value(0, 3, 2).unwrap(), "15");
|
assert_eq!(model.get_formatted_cell_value(0, 3, 2).unwrap(), "15");
|
||||||
assert_eq!(model.formatted_cell_value(0, 4, 2).unwrap(), "58.5");
|
assert_eq!(model.get_formatted_cell_value(0, 4, 2).unwrap(), "58.5");
|
||||||
fs::remove_file(temp_file_name).unwrap();
|
fs::remove_file(temp_file_name).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ use std::{
|
|||||||
use roxmltree::Node;
|
use roxmltree::Node;
|
||||||
|
|
||||||
use ironcalc_base::{
|
use ironcalc_base::{
|
||||||
model::Model,
|
|
||||||
types::{Metadata, Workbook, WorkbookSettings},
|
types::{Metadata, Workbook, WorkbookSettings},
|
||||||
|
Model,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::error::XlsxError;
|
use crate::error::XlsxError;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! # IronCalc - Core API documentation
|
//! # IronCalc - Core API documentation
|
||||||
//!
|
//!
|
||||||
//! This technical API documentation is aimed at developers.
|
//! This technical API documentation is aimed at developers.
|
||||||
//! It is used to build language bindings (like python, javascript or nodejs) or to build full fledged applications like ironCalc in the terminal or IronCalc, the Web application.
|
//! It is used to build language bindings (like python, javascript or nodejs) or to build full fledged applications like TironCalc in the terminal or IronCalc, the Web application.
|
||||||
//!
|
//!
|
||||||
//! ## Basic usage
|
//! ## Basic usage
|
||||||
//!
|
//!
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! [dependencies]
|
//! [dependencies]
|
||||||
//! ironcalc = { git = "https://github.com/ironcalc/IronCalc", version = "0.1"}
|
//! ironcalc = { git = "https://github.com/ironcalc/IronCalc" }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! <small> until version 0.5.0 you should use the git dependencies as stated </small>
|
//! <small> until version 0.5.0 you should use the git dependencies as stated </small>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use uuid::Uuid;
|
|||||||
use ironcalc::compare::{test_file, test_load_and_saving};
|
use ironcalc::compare::{test_file, test_load_and_saving};
|
||||||
use ironcalc::export::save_to_xlsx;
|
use ironcalc::export::save_to_xlsx;
|
||||||
use ironcalc::import::{load_from_excel, load_model_from_xlsx};
|
use ironcalc::import::{load_from_excel, load_model_from_xlsx};
|
||||||
use ironcalc_base::model::Model;
|
|
||||||
use ironcalc_base::types::{HorizontalAlignment, VerticalAlignment, Workbook};
|
use ironcalc_base::types::{HorizontalAlignment, VerticalAlignment, Workbook};
|
||||||
|
use ironcalc_base::Model;
|
||||||
|
|
||||||
// This is a functional test.
|
// This is a functional test.
|
||||||
// We check that the output of example.xlsx is what we expect.
|
// We check that the output of example.xlsx is what we expect.
|
||||||
@@ -179,7 +179,7 @@ fn test_defined_names_casing() {
|
|||||||
model.set_user_input(0, row, column, formula.to_string());
|
model.set_user_input(0, row, column, formula.to_string());
|
||||||
model.evaluate();
|
model.evaluate();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
model.formatted_cell_value(0, row, column).unwrap(),
|
model.get_formatted_cell_value(0, row, column).unwrap(),
|
||||||
expected_value
|
expected_value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user