diff --git a/base/src/expressions/lexer/mod.rs b/base/src/expressions/lexer/mod.rs index fae74f6..0e4dded 100644 --- a/base/src/expressions/lexer/mod.rs +++ b/base/src/expressions/lexer/mod.rs @@ -308,9 +308,9 @@ impl Lexer { return self.consume_range(None); } let name_upper = name.to_ascii_uppercase(); - if name_upper == self.language.booleans.true_value { + if name_upper == self.language.booleans.r#true { return TokenType::Boolean(true); - } else if name_upper == self.language.booleans.false_value { + } else if name_upper == self.language.booleans.r#false { return TokenType::Boolean(false); } if self.mode == LexerMode::A1 { @@ -660,8 +660,8 @@ impl Lexer { fn consume_error(&mut self) -> TokenType { let errors = &self.language.errors; let rest_of_formula: String = self.chars[self.position - 1..self.len].iter().collect(); - if rest_of_formula.starts_with(&errors.ref_value) { - self.position += errors.ref_value.chars().count() - 1; + if rest_of_formula.starts_with(&errors.r#ref) { + self.position += errors.r#ref.chars().count() - 1; return TokenType::Error(Error::REF); } else if rest_of_formula.starts_with(&errors.name) { self.position += errors.name.chars().count() - 1; diff --git a/base/src/expressions/lexer/test/test_locale.rs b/base/src/expressions/lexer/test/test_locale.rs index 24eff09..86c8887 100644 --- a/base/src/expressions/lexer/test/test_locale.rs +++ b/base/src/expressions/lexer/test/test_locale.rs @@ -6,11 +6,11 @@ use crate::{ token::TokenType, }, language::get_language, - locale::get_locale_fix, + locale::get_locale, }; fn new_language_lexer(formula: &str, locale: &str, language: &str) -> Lexer { - let locale = get_locale_fix(locale).unwrap(); + let locale = get_locale(locale).unwrap(); let language = get_language(language).unwrap(); Lexer::new(formula, LexerMode::A1, locale, language) } diff --git a/base/src/expressions/token.rs b/base/src/expressions/token.rs index b4e45ac..639ef2e 100644 --- a/base/src/expressions/token.rs +++ b/base/src/expressions/token.rs @@ -120,7 +120,7 @@ impl Error { pub fn to_localized_error_string(&self, language: &Language) -> String { match self { Error::NULL => language.errors.null.to_string(), - Error::REF => language.errors.ref_value.to_string(), + Error::REF => language.errors.r#ref.to_string(), Error::NAME => language.errors.name.to_string(), Error::VALUE => language.errors.value.to_string(), Error::DIV => language.errors.div.to_string(), @@ -137,7 +137,7 @@ impl Error { pub fn get_error_by_name(name: &str, language: &Language) -> Option { let errors = &language.errors; - if name == errors.ref_value { + if name == errors.r#ref { return Some(Error::REF); } else if name == errors.name { return Some(Error::NAME); diff --git a/base/src/language/mod.rs b/base/src/language/mod.rs index 92ed7a0..e9a4235 100644 --- a/base/src/language/mod.rs +++ b/base/src/language/mod.rs @@ -5,16 +5,13 @@ use std::collections::HashMap; #[derive(Serialize, Deserialize, Clone)] pub struct Booleans { - #[serde(rename = "true")] - pub true_value: String, - #[serde(rename = "false")] - pub false_value: String, + pub r#true: String, + pub r#false: String, } #[derive(Serialize, Deserialize, Clone)] pub struct Errors { - #[serde(rename = "ref")] - pub ref_value: String, + pub r#ref: String, pub name: String, pub value: String, pub div: String, diff --git a/base/src/locale/mod.rs b/base/src/locale/mod.rs index 328ac43..e257126 100644 --- a/base/src/locale/mod.rs +++ b/base/src/locale/mod.rs @@ -80,14 +80,8 @@ static LOCALES: Lazy> = Lazy::new(|| { serde_json::from_str(include_str!("locales.json")).expect("Failed parsing locale") }); -pub fn get_locale(_id: &str) -> Result<&Locale, String> { +pub fn get_locale(id: &str) -> Result<&Locale, String> { // TODO: pass the locale once we implement locales in Rust - let locale = LOCALES.get("en").ok_or("Invalid locale")?; - Ok(locale) -} - -// TODO: Remove this function one we implement locales properly -pub fn get_locale_fix(id: &str) -> Result<&Locale, String> { let locale = LOCALES.get(id).ok_or("Invalid locale")?; Ok(locale) } diff --git a/base/src/model.rs b/base/src/model.rs index eb0f8a2..a50a1b0 100644 --- a/base/src/model.rs +++ b/base/src/model.rs @@ -798,7 +798,7 @@ impl Model { None } - /// Returns a model from a String representation of a workbook + /// Returns a model from an internal binary representation of a workbook /// /// # Examples /// @@ -816,9 +816,12 @@ impl Model { /// # Ok(()) /// # } /// ``` + /// + /// See also: + /// * [Model::to_bytes] pub fn from_bytes(s: &[u8]) -> Result { let workbook: Workbook = - bitcode::decode(s).map_err(|_| "Error parsing workbook".to_string())?; + bitcode::decode(s).map_err(|e| format!("Error parsing workbook: {e}"))?; Model::from_workbook(workbook) } @@ -1760,7 +1763,10 @@ impl Model { .get_style(self.get_cell_style_index(sheet, row, column)) } - /// Returns a JSON string of the workbook + /// Returns an internal binary representation of the workbook + /// + /// See also: + /// * [Model::from_bytes] pub fn to_bytes(&self) -> Vec { bitcode::encode(&self.workbook) } diff --git a/base/src/test/mod.rs b/base/src/test/mod.rs index a3ec5c4..8f3a666 100644 --- a/base/src/test/mod.rs +++ b/base/src/test/mod.rs @@ -53,4 +53,5 @@ mod test_frozen_rows_and_columns; mod test_get_cell_content; mod test_percentage; mod test_today; +mod test_types; mod user_model; diff --git a/base/src/test/test_types.rs b/base/src/test/test_types.rs new file mode 100644 index 0000000..e82f1f2 --- /dev/null +++ b/base/src/test/test_types.rs @@ -0,0 +1,24 @@ +#![allow(clippy::unwrap_used)] + +use crate::types::{Alignment, HorizontalAlignment, VerticalAlignment}; + +#[test] +fn alignment_default() { + let alignment = Alignment::default(); + assert_eq!( + alignment, + Alignment { + horizontal: HorizontalAlignment::General, + vertical: VerticalAlignment::Bottom, + wrap_text: false + } + ); + + let s = serde_json::to_string(&alignment).unwrap(); + // defaults stringifies as an empty object + assert_eq!(s, "{}"); + + let a: Alignment = serde_json::from_str("{}").unwrap(); + + assert_eq!(a, alignment) +} diff --git a/base/src/test/user_model/test_diff_queue.rs b/base/src/test/user_model/test_diff_queue.rs index 147ce93..3760235 100644 --- a/base/src/test/user_model/test_diff_queue.rs +++ b/base/src/test/user_model/test_diff_queue.rs @@ -25,7 +25,7 @@ fn send_queue() { #[test] fn apply_external_diffs_wrong_str() { let mut model1 = UserModel::from_model(new_empty_model()); - assert!(model1.apply_external_diffs("invalid").is_err()); + assert!(model1.apply_external_diffs("invalid".as_bytes()).is_err()); } #[test] @@ -155,5 +155,7 @@ fn new_sheet() { #[test] fn wrong_diffs_handled() { let mut model = UserModel::from_model(new_empty_model()); - assert!(model.apply_external_diffs("Hello world").is_err()); + assert!(model + .apply_external_diffs("Hello world".as_bytes()) + .is_err()); } diff --git a/base/src/test/user_model/test_styles.rs b/base/src/test/user_model/test_styles.rs index 265ce3a..377ff29 100644 --- a/base/src/test/user_model/test_styles.rs +++ b/base/src/test/user_model/test_styles.rs @@ -144,13 +144,18 @@ fn basic_fill() { let style = model.get_cell_style(0, 1, 1).unwrap(); assert_eq!(style.fill.bg_color, None); + assert_eq!(style.fill.fg_color, None); // bg_color model .update_range_style(&range, "fill.bg_color", "#F2F2F2") .unwrap(); + model + .update_range_style(&range, "fill.fg_color", "#F3F4F5") + .unwrap(); let style = model.get_cell_style(0, 1, 1).unwrap(); assert_eq!(style.fill.bg_color, Some("#F2F2F2".to_owned())); + assert_eq!(style.fill.fg_color, Some("#F3F4F5".to_owned())); let send_queue = model.flush_send_queue(); @@ -159,6 +164,7 @@ fn basic_fill() { let style = model2.get_cell_style(0, 1, 1).unwrap(); assert_eq!(style.fill.bg_color, Some("#F2F2F2".to_owned())); + assert_eq!(style.fill.fg_color, Some("#F3F4F5".to_owned())); } #[test] @@ -171,9 +177,15 @@ fn fill_errors() { width: 1, height: 1, }; - assert!(model - .update_range_style(&range, "fill.bg_color", "#FFF") - .is_err()); + assert_eq!( + model.update_range_style(&range, "fill.bg_color", "#FFF"), + Err("Invalid color: '#FFF'.".to_string()) + ); + + assert_eq!( + model.update_range_style(&range, "fill.fg_color", "#FFF"), + Err("Invalid color: '#FFF'.".to_string()) + ); } #[test] diff --git a/base/src/test/user_model/test_to_from_bytes.rs b/base/src/test/user_model/test_to_from_bytes.rs index 637eb15..af4b7bf 100644 --- a/base/src/test/user_model/test_to_from_bytes.rs +++ b/base/src/test/user_model/test_to_from_bytes.rs @@ -25,6 +25,6 @@ fn errors() { let model_bytes = "Early in the morning, late in the century, Cricklewood Broadway.".as_bytes(); assert_eq!( &UserModel::from_bytes(model_bytes).unwrap_err(), - "Error parsing workbook" + "Error parsing workbook: invalid packing" ); } diff --git a/base/src/types.rs b/base/src/types.rs index 54e73da..d8e6318 100644 --- a/base/src/types.rs +++ b/base/src/types.rs @@ -4,37 +4,15 @@ use std::{collections::HashMap, fmt::Display}; use crate::expressions::token::Error; -// Useful for `#[serde(default = "default_as_true")]` -fn default_as_true() -> bool { - true -} - fn default_as_false() -> bool { false } -// Useful for `#[serde(skip_serializing_if = "is_true")]` -fn is_true(b: &bool) -> bool { - *b -} - fn is_false(b: &bool) -> bool { !*b } -fn is_zero(num: &i32) -> bool { - *num == 0 -} - -fn is_default_alignment(o: &Option) -> bool { - o.is_none() || *o == Some(Alignment::default()) -} - -fn hashmap_is_empty(h: &HashMap) -> bool { - h.values().len() == 0 -} - -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct Metadata { pub application: String, pub app_version: String, @@ -44,14 +22,13 @@ pub struct Metadata { pub last_modified: String, //"2020-11-20T16:24:35" } -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct WorkbookSettings { pub tz: String, pub locale: String, } /// An internal representation of an IronCalc Workbook -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)] -#[serde(deny_unknown_fields)] +#[derive(Encode, Decode, Debug, PartialEq, Clone)] pub struct Workbook { pub shared_strings: Vec, pub defined_names: Vec, @@ -60,17 +37,14 @@ pub struct Workbook { pub name: String, pub settings: WorkbookSettings, pub metadata: Metadata, - #[serde(default)] - #[serde(skip_serializing_if = "hashmap_is_empty")] pub tables: HashMap, } /// A defined name. The `sheet_id` is the sheet index in case the name is local -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct DefinedName { pub name: String, pub formula: String, - #[serde(skip_serializing_if = "Option::is_none")] pub sheet_id: Option, } @@ -80,8 +54,7 @@ pub struct DefinedName { /// * state: /// 18.18.68 ST_SheetState (Sheet Visibility Types) /// hidden, veryHidden, visible -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] -#[serde(rename_all = "lowercase")] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub enum SheetState { Visible, Hidden, @@ -99,7 +72,7 @@ impl Display for SheetState { } /// Internal representation of a worksheet Excel object -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Clone)] pub struct Worksheet { pub dimension: String, pub cols: Vec, @@ -109,15 +82,10 @@ pub struct Worksheet { pub shared_formulas: Vec, pub sheet_id: u32, pub state: SheetState, - #[serde(skip_serializing_if = "Option::is_none")] pub color: Option, pub merge_cells: Vec, pub comments: Vec, - #[serde(default)] - #[serde(skip_serializing_if = "is_zero")] pub frozen_rows: i32, - #[serde(default)] - #[serde(skip_serializing_if = "is_zero")] pub frozen_columns: i32, } @@ -126,7 +94,7 @@ pub struct Worksheet { pub type SheetData = HashMap>; // ECMA-376-1:2016 section 18.3.1.73 -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Clone)] pub struct Row { /// Row index pub r: i32, @@ -134,23 +102,19 @@ pub struct Row { pub custom_format: bool, pub custom_height: bool, pub s: i32, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub hidden: bool, } // ECMA-376-1:2016 section 18.3.1.13 -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Clone)] pub struct Col { // Column definitions are defined on ranges, unlike rows which store unique, per-row entries. /// First column affected by this record. Settings apply to column in \[min, max\] range. pub min: i32, /// Last column affected by this record. Settings apply to column in \[min, max\] range. pub max: i32, - pub width: f64, pub custom_width: bool, - #[serde(skip_serializing_if = "Option::is_none")] pub style: Option, } @@ -165,32 +129,55 @@ pub enum CellType { CompoundData = 128, } -#[derive(Serialize, Deserialize, Encode, Decode, Debug, Clone, PartialEq)] -#[serde(tag = "t", deny_unknown_fields)] +#[derive(Encode, Decode, Debug, Clone, PartialEq)] pub enum Cell { - #[serde(rename = "empty")] - EmptyCell { s: i32 }, - #[serde(rename = "b")] - BooleanCell { v: bool, s: i32 }, - #[serde(rename = "n")] - NumberCell { v: f64, s: i32 }, + EmptyCell { + s: i32, + }, + + BooleanCell { + v: bool, + s: i32, + }, + + NumberCell { + v: f64, + s: i32, + }, // Maybe we should not have this type. In Excel this is just a string - #[serde(rename = "e")] - ErrorCell { ei: Error, s: i32 }, + ErrorCell { + ei: Error, + s: i32, + }, // Always a shared string - #[serde(rename = "s")] - SharedString { si: i32, s: i32 }, + SharedString { + si: i32, + s: i32, + }, // Non evaluated Formula - #[serde(rename = "u")] - CellFormula { f: i32, s: i32 }, - #[serde(rename = "fb")] - CellFormulaBoolean { f: i32, v: bool, s: i32 }, - #[serde(rename = "fn")] - CellFormulaNumber { f: i32, v: f64, s: i32 }, + CellFormula { + f: i32, + s: i32, + }, + + CellFormulaBoolean { + f: i32, + v: bool, + s: i32, + }, + + CellFormulaNumber { + f: i32, + v: f64, + s: i32, + }, // always inline string - #[serde(rename = "str")] - CellFormulaString { f: i32, v: String, s: i32 }, - #[serde(rename = "fe")] + CellFormulaString { + f: i32, + v: String, + s: i32, + }, + CellFormulaError { f: i32, ei: Error, @@ -209,17 +196,16 @@ impl Default for Cell { } } -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct Comment { pub text: String, pub author_name: String, - #[serde(skip_serializing_if = "Option::is_none")] pub author_id: Option, pub cell_ref: String, } // ECMA-376-1:2016 section 18.5.1.2 -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct Table { pub name: String, pub display_name: String, @@ -227,34 +213,24 @@ pub struct Table { pub reference: String, pub totals_row_count: u32, pub header_row_count: u32, - #[serde(skip_serializing_if = "Option::is_none")] pub header_row_dxf_id: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub data_dxf_id: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub totals_row_dxf_id: Option, pub columns: Vec, pub style_info: TableStyleInfo, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub has_filters: bool, } // totals_row_label vs totals_row_function might be mutually exclusive. Use an enum? // the totals_row_function is an enum not String methinks -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct TableColumn { pub id: u32, pub name: String, - #[serde(skip_serializing_if = "Option::is_none")] pub totals_row_label: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub header_row_dxf_id: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub data_dxf_id: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub totals_row_dxf_id: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub totals_row_function: Option, } @@ -272,25 +248,16 @@ impl Default for TableColumn { } } -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, Default)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Default)] pub struct TableStyleInfo { - #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub show_first_column: bool, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub show_last_column: bool, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub show_row_stripes: bool, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub show_column_stripes: bool, } -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct Styles { pub num_fmts: Vec, pub fonts: Vec, @@ -326,7 +293,7 @@ pub struct Style { pub quote_prefix: bool, } -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct NumFmt { pub num_fmt_id: i32, pub format_code: String, @@ -516,29 +483,17 @@ pub struct Alignment { pub wrap_text: bool, } -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct CellStyleXfs { pub num_fmt_id: i32, pub font_id: i32, pub fill_id: i32, pub border_id: i32, - #[serde(default = "default_as_true")] - #[serde(skip_serializing_if = "is_true")] pub apply_number_format: bool, - #[serde(default = "default_as_true")] - #[serde(skip_serializing_if = "is_true")] pub apply_border: bool, - #[serde(default = "default_as_true")] - #[serde(skip_serializing_if = "is_true")] pub apply_alignment: bool, - #[serde(default = "default_as_true")] - #[serde(skip_serializing_if = "is_true")] pub apply_protection: bool, - #[serde(default = "default_as_true")] - #[serde(skip_serializing_if = "is_true")] pub apply_font: bool, - #[serde(default = "default_as_true")] - #[serde(skip_serializing_if = "is_true")] pub apply_fill: bool, } @@ -559,39 +514,24 @@ impl Default for CellStyleXfs { } } -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, Default)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Default)] pub struct CellXfs { pub xf_id: i32, pub num_fmt_id: i32, pub font_id: i32, pub fill_id: i32, pub border_id: i32, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub apply_number_format: bool, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub apply_border: bool, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub apply_alignment: bool, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub apply_protection: bool, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub apply_font: bool, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub apply_fill: bool, - #[serde(default = "default_as_false")] - #[serde(skip_serializing_if = "is_false")] pub quote_prefix: bool, - #[serde(skip_serializing_if = "is_default_alignment")] pub alignment: Option, } -#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct CellStyles { pub name: String, pub xf_id: i32, diff --git a/base/src/user_model.rs b/base/src/user_model.rs index 3bc29e8..3e73592 100644 --- a/base/src/user_model.rs +++ b/base/src/user_model.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, fmt::Debug}; -use serde::{Deserialize, Serialize}; +use bitcode::{Decode, Encode}; use crate::{ constants, @@ -18,19 +18,19 @@ use crate::{ utils::is_valid_hex_color, }; -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Encode, Decode)] struct RowData { row: Option, data: HashMap, } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Encode, Decode)] struct ColumnData { column: Option, data: HashMap, } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Encode, Decode)] enum Diff { // Cell diffs SetCellValue { @@ -160,13 +160,13 @@ impl History { } } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Encode, Decode)] enum DiffType { Undo, Redo, } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Encode, Decode)] struct QueueDiffs { r#type: DiffType, list: DiffList, @@ -408,9 +408,9 @@ impl UserModel { /// /// See also: /// * [UserModel::apply_external_diffs] - pub fn flush_send_queue(&mut self) -> String { + pub fn flush_send_queue(&mut self) -> Vec { // This can never fail :O: - let q = serde_json::to_string(&self.send_queue).unwrap(); + let q = bitcode::encode(&self.send_queue); self.send_queue = vec![]; q } @@ -421,8 +421,8 @@ impl UserModel { /// /// See also: /// * [UserModel::flush_send_queue] - pub fn apply_external_diffs(&mut self, diff_list_str: &str) -> Result<(), String> { - if let Ok(queue_diffs_list) = serde_json::from_str::>(diff_list_str) { + pub fn apply_external_diffs(&mut self, diff_list_str: &[u8]) -> Result<(), String> { + if let Ok(queue_diffs_list) = bitcode::decode::>(diff_list_str) { for queue_diff in queue_diffs_list { if matches!(queue_diff.r#type, DiffType::Redo) { self.apply_diff_list(&queue_diff.list)?; @@ -845,6 +845,9 @@ impl UserModel { "fill.bg_color" => { style.fill.bg_color = color(value)?; } + "fill.fg_color" => { + style.fill.fg_color = color(value)?; + } "num_fmt" => { style.num_fmt = value.to_owned(); } diff --git a/bindings/wasm/Makefile b/bindings/wasm/Makefile index 0b251de..8e91a88 100644 --- a/bindings/wasm/Makefile +++ b/bindings/wasm/Makefile @@ -1,9 +1,12 @@ all: - wasm-pack build --target web --scope ironcalc + wasm-pack build --target web --scope ironcalc --release cp README.pkg.md pkg/README.md tsc types.ts --target esnext --module esnext python fix_types.py +tests: + wasm-pack build --target nodejs && node tests/test.mjs + lint: cargo check cargo fmt -- --check diff --git a/bindings/wasm/fix_types.py b/bindings/wasm/fix_types.py index 0cb6de1..41048f6 100644 --- a/bindings/wasm/fix_types.py +++ b/bindings/wasm/fix_types.py @@ -14,9 +14,9 @@ export function getTokens(formula: string): any; """.strip() get_tokens_str_types = r""" -* @returns {TokenType[]} +* @returns {MarkedToken[]} */ -export function getTokens(formula: string): TokenType[]; +export function getTokens(formula: string): MarkedToken[]; """.strip() update_style_str = r""" diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 47c256c..a09aaec 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -71,12 +71,12 @@ impl Model { } #[wasm_bindgen(js_name = "flushSendQueue")] - pub fn flush_send_queue(&mut self) -> String { + pub fn flush_send_queue(&mut self) -> Vec { self.model.flush_send_queue() } #[wasm_bindgen(js_name = "applyExternalDiffs")] - pub fn apply_external_diffs(&mut self, diffs: &str) -> Result<(), JsError> { + pub fn apply_external_diffs(&mut self, diffs: &[u8]) -> Result<(), JsError> { self.model.apply_external_diffs(diffs).map_err(to_js_error) } diff --git a/xlsx/src/bin/test.rs b/xlsx/src/bin/test.rs index 2b094fa..c7703cc 100644 --- a/xlsx/src/bin/test.rs +++ b/xlsx/src/bin/test.rs @@ -8,7 +8,7 @@ use std::path; -use ironcalc::{compare::test_file, export::save_to_xlsx, import::load_model_from_xlsx}; +use ironcalc::{compare::test_file, export::save_to_xlsx, import::load_from_xlsx}; fn main() { let args: Vec<_> = std::env::args().collect(); @@ -27,7 +27,7 @@ fn main() { let file_path = path::Path::new(file_name); let base_name = file_path.file_stem().unwrap().to_str().unwrap(); let output_file_name = &format!("{base_name}.output.xlsx"); - let mut model = load_model_from_xlsx(file_name, "en", "UTC").unwrap(); + let mut model = load_from_xlsx(file_name, "en", "UTC").unwrap(); model.evaluate(); println!("Saving result as: {output_file_name}. Please open with Excel and test."); save_to_xlsx(&model, output_file_name).unwrap(); diff --git a/xlsx/src/bin/xlsx_2_icalc.rs b/xlsx/src/bin/xlsx_2_icalc.rs index 7368aba..d760770 100644 --- a/xlsx/src/bin/xlsx_2_icalc.rs +++ b/xlsx/src/bin/xlsx_2_icalc.rs @@ -8,7 +8,7 @@ use std::path; -use ironcalc::{export::save_to_json, import::load_model_from_xlsx}; +use ironcalc::{export::save_to_icalc, import::load_from_xlsx}; fn main() { let args: Vec<_> = std::env::args().collect(); @@ -21,6 +21,6 @@ fn main() { let file_path = path::Path::new(file_name); let base_name = file_path.file_stem().unwrap().to_str().unwrap(); let output_file_name = &format!("{base_name}.ic"); - let model = load_model_from_xlsx(file_name, "en", "UTC").unwrap(); - save_to_json(model.workbook, output_file_name); + let model = load_from_xlsx(file_name, "en", "UTC").unwrap(); + save_to_icalc(model.workbook, output_file_name); } diff --git a/xlsx/src/compare.rs b/xlsx/src/compare.rs index 083c7d4..2b1a47e 100644 --- a/xlsx/src/compare.rs +++ b/xlsx/src/compare.rs @@ -5,7 +5,7 @@ use ironcalc_base::types::*; use ironcalc_base::{expressions::utils::number_to_column, Model}; use crate::export::save_to_xlsx; -use crate::import::load_model_from_xlsx; +use crate::import::load_from_xlsx; pub struct CompareError { message: String, @@ -164,13 +164,13 @@ pub(crate) fn compare_models(m1: &Model, m2: &Model) -> Result<(), String> { let mut message = "".to_string(); for diff in diffs { message = format!( - "{}\n.Diff: {}!{}{}, value1: {}, value2 {}\n {}", + "{}\n.Diff: {}!{}{}, value1: {:?}, value2 {:?}\n {}", message, diff.sheet_name, number_to_column(diff.column).unwrap(), diff.row, - serde_json::to_string(&diff.value1).unwrap(), - serde_json::to_string(&diff.value2).unwrap(), + &diff.value1, + &diff.value2, diff.reason ); } @@ -183,15 +183,15 @@ pub(crate) fn compare_models(m1: &Model, m2: &Model) -> Result<(), String> { /// Tests that file in file_path produces the same results in Excel and in IronCalc. pub fn test_file(file_path: &str) -> Result<(), String> { - let model1 = load_model_from_xlsx(file_path, "en", "UTC").unwrap(); - let mut model2 = load_model_from_xlsx(file_path, "en", "UTC").unwrap(); + let model1 = load_from_xlsx(file_path, "en", "UTC").unwrap(); + let mut model2 = load_from_xlsx(file_path, "en", "UTC").unwrap(); model2.evaluate(); compare_models(&model1, &model2) } /// Tests that file in file_path can be converted to xlsx and read again pub fn test_load_and_saving(file_path: &str, temp_dir_name: &Path) -> Result<(), String> { - let model1 = load_model_from_xlsx(file_path, "en", "UTC").unwrap(); + let model1 = load_from_xlsx(file_path, "en", "UTC").unwrap(); let base_name = Path::new(file_path).file_name().unwrap().to_str().unwrap(); @@ -200,7 +200,7 @@ pub fn test_load_and_saving(file_path: &str, temp_dir_name: &Path) -> Result<(), // test can save save_to_xlsx(&model1, temp_file_path).unwrap(); // test can open - let mut model2 = load_model_from_xlsx(temp_file_path, "en", "UTC").unwrap(); + let mut model2 = load_from_xlsx(temp_file_path, "en", "UTC").unwrap(); model2.evaluate(); compare_models(&model1, &model2) } diff --git a/xlsx/src/export/mod.rs b/xlsx/src/export/mod.rs index 1596897..4dad2a3 100644 --- a/xlsx/src/export/mod.rs +++ b/xlsx/src/export/mod.rs @@ -129,9 +129,9 @@ pub fn save_xlsx_to_writer(model: &Model, writer: W) -> Result< } /// Exports an internal representation of a workbook into an equivalent IronCalc json format -pub fn save_to_json(workbook: Workbook, output: &str) { - let s = serde_json::to_string(&workbook).unwrap(); +pub fn save_to_icalc(workbook: Workbook, output: &str) { + let s = bitcode::encode(&workbook); let file_path = std::path::Path::new(output); let mut file = fs::File::create(file_path).unwrap(); - file.write_all(s.as_bytes()).unwrap(); + file.write_all(&s).unwrap(); } diff --git a/xlsx/src/export/test/test_export.rs b/xlsx/src/export/test/test_export.rs index 872b702..09eac2e 100644 --- a/xlsx/src/export/test/test_export.rs +++ b/xlsx/src/export/test/test_export.rs @@ -3,7 +3,9 @@ use std::fs; use ironcalc_base::Model; use crate::error::XlsxError; -use crate::{export::save_to_xlsx, import::load_model_from_xlsx}; +use crate::export::save_to_icalc; +use crate::import::load_from_icalc; +use crate::{export::save_to_xlsx, import::load_from_xlsx}; pub fn new_empty_model() -> Model { Model::new_empty("model", "en", "UTC").unwrap() @@ -26,29 +28,54 @@ fn test_values() { // noop model.evaluate(); + { + let temp_file_name = "temp_file_test_values.xlsx"; + save_to_xlsx(&model, temp_file_name).unwrap(); - let temp_file_name = "temp_file_test_values.xlsx"; - save_to_xlsx(&model, temp_file_name).unwrap(); + let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap(); + assert_eq!(model.get_formatted_cell_value(0, 1, 1).unwrap(), "123.456"); + assert_eq!( + model.get_formatted_cell_value(0, 2, 1).unwrap(), + "Hello world!" + ); + assert_eq!( + 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!"); - let model = load_model_from_xlsx(temp_file_name, "en", "UTC").unwrap(); - assert_eq!(model.get_formatted_cell_value(0, 1, 1).unwrap(), "123.456"); - assert_eq!( - model.get_formatted_cell_value(0, 2, 1).unwrap(), - "Hello world!" - ); - assert_eq!( - 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(); + } + { + let temp_file_name = "temp_file_test_values.ic"; + save_to_icalc(model.workbook, temp_file_name); - fs::remove_file(temp_file_name).unwrap(); + let model = load_from_icalc(temp_file_name).unwrap(); + assert_eq!(model.get_formatted_cell_value(0, 1, 1).unwrap(), "123.456"); + assert_eq!( + model.get_formatted_cell_value(0, 2, 1).unwrap(), + "Hello world!" + ); + assert_eq!( + 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(); + } } #[test] @@ -67,7 +94,7 @@ fn test_formulas() { let temp_file_name = "temp_file_test_formulas.xlsx"; save_to_xlsx(&model, temp_file_name).unwrap(); - let model = load_model_from_xlsx(temp_file_name, "en", "UTC").unwrap(); + let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap(); assert_eq!(model.get_formatted_cell_value(0, 1, 2).unwrap(), "11"); assert_eq!(model.get_formatted_cell_value(0, 2, 2).unwrap(), "13"); assert_eq!(model.get_formatted_cell_value(0, 3, 2).unwrap(), "15"); @@ -89,7 +116,7 @@ fn test_sheets() { let temp_file_name = "temp_file_test_sheets.xlsx"; save_to_xlsx(&model, temp_file_name).unwrap(); - let model = load_model_from_xlsx(temp_file_name, "en", "UTC").unwrap(); + let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap(); assert_eq!( model.workbook.get_worksheet_names(), vec!["Sheet1", "With space", "Tango & Cash", "你好世界"] @@ -118,7 +145,7 @@ fn test_named_styles() { let temp_file_name = "temp_file_test_named_styles.xlsx"; save_to_xlsx(&model, temp_file_name).unwrap(); - let model = load_model_from_xlsx(temp_file_name, "en", "UTC").unwrap(); + let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap(); assert!(model .workbook .styles diff --git a/xlsx/src/import/mod.rs b/xlsx/src/import/mod.rs index 99fec12..9996c1a 100644 --- a/xlsx/src/import/mod.rs +++ b/xlsx/src/import/mod.rs @@ -106,7 +106,7 @@ fn load_xlsx_from_reader( // Public methods /// Imports a file from disk into an internal representation -pub fn load_from_excel(file_name: &str, locale: &str, tz: &str) -> Result { +fn load_from_excel(file_name: &str, locale: &str, tz: &str) -> Result { let file_path = std::path::Path::new(file_name); let file = fs::File::open(file_path)?; let reader = BufReader::new(file); @@ -118,7 +118,14 @@ pub fn load_from_excel(file_name: &str, locale: &str, tz: &str) -> Result Result { +pub fn load_from_xlsx(file_name: &str, locale: &str, tz: &str) -> Result { let workbook = load_from_excel(file_name, locale, tz)?; Model::from_workbook(workbook).map_err(XlsxError::Workbook) } + +pub fn load_from_icalc(file_name: &str) -> Result { + let contents = fs::read(file_name) + .map_err(|e| XlsxError::IO(format!("Could not extract workbook name: {}", e)))?; + let workbook: Workbook = bitcode::decode(&contents).unwrap(); + Model::from_workbook(workbook).map_err(XlsxError::Workbook) +} diff --git a/xlsx/tests/example.ic b/xlsx/tests/example.ic new file mode 100644 index 0000000..6da53d0 Binary files /dev/null and b/xlsx/tests/example.ic differ diff --git a/xlsx/tests/example.json b/xlsx/tests/example.json deleted file mode 100644 index 00b9b34..0000000 --- a/xlsx/tests/example.json +++ /dev/null @@ -1,2524 +0,0 @@ -{ - "settings": { - "tz": "UTC", - "locale": "en" - }, - "metadata": { - "application": "Microsoft Excel", - "app_version": "16.0300", - "creator": "nicol", - "last_modified_by": "nicol", - "created": "2020-08-06T21:20:53Z", - "last_modified": "2020-11-20T16:24:35Z" - }, - "tables": { - "Table1": { - "name": "Table1", - "display_name": "Table1", - "sheet_name": "Table", - "reference": "A1:D4", - "totals_row_count": 0, - "header_row_count": 1, - "columns": [ - { "id": 1, "name": "Cars" }, - { "id": 2, "name": "Year" }, - { "id": 3, "name": "Make" }, - { "id": 4, "name": "Other" } - ], - "style_info": { - "show_first_column": false, - "show_last_column": false, - "show_row_stripes": true, - "show_column_stripes": false - } - } - }, - "shared_strings": [ - "A string", - "Long column", - "Short column", - "Red", - "in", - "yellow", - "merged cells", - "Bold text", - "Some colours", - "quantum", - "A def name", - "Hola Tu", - "High Row", - "Tres", - "Noch eine", - "sredtg", - "This was created fourth, in the third position then moved second", - "Units", - "Hola", - "Cars", - "Year", - "Make", - "R5", - "Renault", - "C6", - "Citroen", - "MB", - "Mercedes", - "Other", - "A", - "B", - "Row Labels", - "Grand Total", - "Sum of Year", - "Just a coment", - "42 too", - "Red only row", - "Also read only row", - "Read only cell", - "Original Themes", - "Standard Colors", - "With a tint" - ], - "defined_names": [ - { - "name": "answer", - "formula": "shared!$G$5", - "sheet_id": 9 - }, - { - "name": "answer2", - "formula": "Sheet1!$I$6", - "sheet_id": 1 - }, - { - "name": "local_thing", - "formula": "Second!$B$1:$B$9", - "sheet_id": 3 - }, - { - "name": "numbers", - "formula": "Sheet1!$A$16:$A$18" - }, - { - "name": "quantum", - "formula": "Sheet1!$C$14" - } - ], - "worksheets": [ - { - "dimension": "A1:O33", - "cols": [ - { - "min": 5, - "max": 5, - "width": 38.26953125, - "custom_width": true - }, - { - "min": 6, - "max": 6, - "width": 9.1796875, - "custom_width": false, - "style": 1 - }, - { - "min": 8, - "max": 8, - "width": 4, - "custom_width": true - } - ], - "rows": [ - { - "height": 13, - "r": 7, - "custom_format": false, - "custom_height": true, - "s": 0 - }, - { - "height": 20, - "r": 8, - "custom_format": false, - "custom_height": true, - "s": 0 - }, - { - "height": 29.5, - "r": 9, - "custom_format": false, - "custom_height": true, - "s": 0 - }, - { - "height": 21.0, - "r": 15, - "custom_format": false, - "custom_height": false, - "s": 0 - }, - { - "height": 15.0, - "r": 17, - "custom_format": false, - "custom_height": false, - "s": 0 - }, - { - "height": 15.0, - "r": 20, - "custom_format": false, - "custom_height": false, - "s": 0 - }, - { - "height": 15.0, - "r": 22, - "custom_format": false, - "custom_height": false, - "s": 0 - }, - { - "height": 15.5, - "r": 23, - "custom_format": false, - "custom_height": false, - "s": 0 - }, - { - "height": 15.0, - "r": 24, - "custom_format": false, - "custom_height": false, - "s": 0 - } - ], - "name": "Sheet1", - "sheet_data": { - "1": { - "1": { - "t": "s", - "si": 0, - "s": 0 - }, - "3": { - "t": "n", - "v": 1, - "s": 0 - }, - "4": { - "t": "fn", - "f": 0, - "v": 2, - "s": 0 - }, - "5": { - "t": "s", - "si": 1, - "s": 0 - }, - "6": { - "t": "s", - "si": 3, - "s": 1 - }, - "8": { - "t": "s", - "si": 2, - "s": 0 - } - }, - "2": { - "1": { - "t": "n", - "v": 222, - "s": 0 - }, - "3": { - "t": "n", - "v": 2, - "s": 0 - }, - "4": { - "t": "fn", - "f": 0, - "v": 3, - "s": 0 - }, - "6": { - "t": "s", - "si": 4, - "s": 1 - }, - "12": { - "t": "s", - "si": 7, - "s": 2 - } - }, - "3": { - "2": { - "t": "fn", - "f": 1, - "v": 0, - "s": 0 - }, - "3": { - "t": "n", - "v": 3, - "s": 0 - }, - "4": { - "t": "fn", - "f": 0, - "v": 4, - "s": 0 - }, - "6": { - "t": "s", - "si": 5, - "s": 1 - } - }, - "6": { - "9": { - "t": "s", - "si": 35, - "s": 0 - } - }, - "7": { - "11": { - "t": "s", - "si": 6, - "s": 17 - }, - "12": { - "t": "empty", - "s": 17 - } - }, - "8": { - "11": { - "t": "empty", - "s": 17 - }, - "12": { - "t": "empty", - "s": 17 - }, - "14": { - "t": "empty", - "s": 3 - }, - "15": { - "t": "empty", - "s": 3 - } - }, - "9": { - "5": { - "t": "s", - "si": 12, - "s": 0 - }, - "11": { - "t": "empty", - "s": 17 - }, - "12": { - "t": "empty", - "s": 17 - }, - "14": { - "t": "s", - "si": 8, - "s": 3 - }, - "15": { - "t": "empty", - "s": 3 - } - }, - "10": { - "11": { - "t": "empty", - "s": 17 - }, - "12": { - "t": "empty", - "s": 17 - } - }, - "13": { - "3": { - "t": "s", - "si": 10, - "s": 0 - } - }, - "14": { - "3": { - "t": "s", - "si": 9, - "s": 0 - }, - "4": { - "t": "str", - "f": 2, - "v": "quantum", - "s": 0 - } - }, - "15": { - "11": { - "t": "s", - "si": 11, - "s": 0 - } - }, - "16": { - "1": { - "t": "n", - "v": 2, - "s": 0 - }, - "8": { - "t": "empty", - "s": 12 - } - }, - "17": { - "1": { - "t": "n", - "v": 6, - "s": 0 - }, - "8": { - "t": "empty", - "s": 14 - } - }, - "18": { - "1": { - "t": "n", - "v": 7, - "s": 0 - }, - "3": { - "t": "fn", - "f": 3, - "v": 15, - "s": 0 - }, - "8": { - "t": "n", - "v": 1, - "s": 18 - }, - "9": { - "t": "empty", - "s": 19 - }, - "10": { - "t": "empty", - "s": 20 - } - }, - "19": { - "8": { - "t": "empty", - "s": 21 - }, - "9": { - "t": "empty", - "s": 22 - }, - "10": { - "t": "empty", - "s": 23 - } - }, - "20": { - "8": { - "t": "empty", - "s": 24 - }, - "9": { - "t": "empty", - "s": 25 - }, - "10": { - "t": "empty", - "s": 26 - } - }, - "21": { - "8": { - "t": "empty", - "s": 13 - } - }, - "22": { - "8": { - "t": "empty", - "s": 12 - } - }, - "23": { - "2": { - "t": "s", - "si": 18, - "s": 5 - }, - "3": { - "t": "empty", - "s": 6 - } - }, - "24": {}, - "26": { - "1": { - "t": "e", - "ei": 2, - "s": 0 - }, - "3": { - "t": "str", - "f": 4, - "v": "#N/A", - "s": 0 - }, - "4": { - "t": "fb", - "f": 5, - "v": true, - "s": 0 - } - }, - "27": { - "1": { - "t": "fe", - "f": 6, - "ei": 4, - "s": 0, - "o": "Sheet1!A27", - "m": "#N/A" - }, - "2": { - "t": "fe", - "f": 6, - "ei": 4, - "s": 0, - "o": "Sheet1!B27", - "m": "#N/A" - }, - "3": { - "t": "e", - "ei": 4, - "s": 0 - }, - "4": { - "t": "fb", - "f": 5, - "v": true, - "s": 0 - } - }, - "28": { - "1": { - "t": "fe", - "f": 6, - "ei": 4, - "s": 0, - "o": "Sheet1!A28", - "m": "#N/A" - }, - "2": { - "t": "fe", - "f": 6, - "ei": 4, - "s": 0, - "o": "Sheet1!B28", - "m": "#N/A" - }, - "4": { - "t": "fb", - "f": 5, - "v": true, - "s": 0 - } - }, - "29": { - "1": { - "t": "fe", - "f": 6, - "ei": 4, - "s": 0, - "o": "Sheet1!A29", - "m": "#N/A" - }, - "2": { - "t": "fe", - "f": 6, - "ei": 4, - "s": 0, - "o": "Sheet1!B29", - "m": "#N/A" - }, - "4": { - "t": "fb", - "f": 5, - "v": true, - "s": 0 - } - }, - "32": { - "4": { - "t": "fb", - "f": 7, - "v": false, - "s": 0 - } - }, - "33": { - "4": { - "t": "fb", - "f": 7, - "v": true, - "s": 0 - } - } - }, - "shared_formulas": [ - "R[0]C[-1]+1", - "R[0]C[-1]", - "quantum", - "SUM(numbers)", - "\"#N/A\"", - "ISERROR(R[0]C[-3])", - "NA()", - "ISERROR(R[-6]C[-1])" - ], - "sheet_id": 1, - "state": "visible", - "merge_cells": ["K7:L10", "H18:J20"], - "comments": [ - { - "text": "nicol:\nThis cell has bold! Text. Náguara!", - "author_name": "", - "cell_ref": "L2" - }, - { - "text": "nicol:\nThere is a coment here, you know\n", - "author_name": "", - "cell_ref": "K7" - }, - { - "text": "nicol:\nNew comment", - "author_name": "", - "cell_ref": "E9" - }, - { - "text": "nicol:\nThere is a 15 here.\n", - "author_name": "", - "cell_ref": "C18" - } - ] - }, - { - "dimension": "A1:R19", - "cols": [ - { - "min": 1, - "max": 1, - "width": 17.26953125, - "custom_width": true - }, - { - "min": 4, - "max": 4, - "width": 18.54296875, - "custom_width": true - }, - { - "min": 8, - "max": 8, - "width": 25.6328125, - "custom_width": true - } - ], - "rows": [], - "name": "Second", - "sheet_data": { - "1": { - "1": { - "t": "s", - "si": 13, - "s": 0 - }, - "2": { - "t": "n", - "v": 1, - "s": 0 - }, - "4": { - "t": "n", - "v": 1, - "s": 0 - }, - "5": { - "t": "fn", - "f": 0, - "v": 45, - "s": 0 - }, - "8": { - "t": "s", - "si": 39, - "s": 0 - }, - "9": { - "t": "empty", - "s": 27 - }, - "10": { - "t": "empty", - "s": 28 - }, - "11": { - "t": "empty", - "s": 29 - }, - "12": { - "t": "empty", - "s": 30 - }, - "13": { - "t": "empty", - "s": 31 - }, - "14": { - "t": "empty", - "s": 32 - }, - "15": { - "t": "empty", - "s": 33 - }, - "16": { - "t": "empty", - "s": 34 - }, - "17": { - "t": "empty", - "s": 35 - }, - "18": { - "t": "empty", - "s": 36 - } - }, - "2": { - "1": { - "t": "s", - "si": 14, - "s": 0 - }, - "2": { - "t": "n", - "v": 2, - "s": 0 - }, - "4": { - "t": "n", - "v": -2, - "s": 0 - }, - "8": { - "t": "s", - "si": 41, - "s": 0 - }, - "9": { - "t": "empty", - "s": 37 - }, - "10": { - "t": "empty", - "s": 38 - }, - "11": { - "t": "empty", - "s": 39 - }, - "12": { - "t": "empty", - "s": 40 - }, - "13": { - "t": "empty", - "s": 41 - }, - "14": { - "t": "empty", - "s": 42 - }, - "15": { - "t": "empty", - "s": 43 - }, - "16": { - "t": "empty", - "s": 44 - }, - "17": { - "t": "empty", - "s": 45 - }, - "18": { - "t": "empty", - "s": 46 - } - }, - "3": { - "2": { - "t": "n", - "v": 3, - "s": 0 - }, - "4": { - "t": "n", - "v": 3, - "s": 0 - } - }, - "4": { - "2": { - "t": "n", - "v": 4, - "s": 0 - }, - "4": { - "t": "n", - "v": -4, - "s": 0 - } - }, - "5": { - "2": { - "t": "n", - "v": 5, - "s": 0 - }, - "4": { - "t": "n", - "v": -2, - "s": 0 - } - }, - "6": { - "2": { - "t": "n", - "v": 6, - "s": 0 - }, - "4": { - "t": "n", - "v": -3, - "s": 0 - }, - "7": { - "t": "s", - "si": 15, - "s": 4 - } - }, - "7": { - "2": { - "t": "n", - "v": 7, - "s": 0 - }, - "8": { - "t": "s", - "si": 40, - "s": 0 - }, - "9": { - "t": "empty", - "s": 47 - }, - "10": { - "t": "empty", - "s": 48 - }, - "11": { - "t": "empty", - "s": 49 - }, - "12": { - "t": "empty", - "s": 50 - }, - "13": { - "t": "empty", - "s": 51 - }, - "14": { - "t": "empty", - "s": 52 - }, - "15": { - "t": "empty", - "s": 53 - }, - "16": { - "t": "empty", - "s": 54 - }, - "17": { - "t": "empty", - "s": 55 - }, - "18": { - "t": "empty", - "s": 56 - } - }, - "8": { - "2": { - "t": "n", - "v": 8, - "s": 0 - } - }, - "9": { - "2": { - "t": "n", - "v": 9, - "s": 0 - } - }, - "16": { - "1": { - "t": "s", - "si": 17, - "s": 0 - } - }, - "17": { - "1": { - "t": "n", - "v": 1, - "s": 0 - }, - "2": { - "t": "n", - "v": 7, - "s": 0 - } - }, - "18": { - "1": { - "t": "n", - "v": 2, - "s": 0 - }, - "2": { - "t": "n", - "v": 8, - "s": 0 - } - }, - "19": { - "1": { - "t": "n", - "v": 3, - "s": 0 - }, - "2": { - "t": "n", - "v": 9, - "s": 0 - } - } - }, - "shared_formulas": ["SUM(local_thing)"], - "sheet_id": 3, - "state": "visible", - "merge_cells": [], - "comments": [] - }, - { - "dimension": "A3:B13", - "cols": [ - { - "min": 1, - "max": 1, - "width": 12.81640625, - "custom_width": true - }, - { - "min": 2, - "max": 2, - "width": 10.81640625, - "custom_width": true - } - ], - "rows": [], - "name": "Sheet4", - "sheet_data": { - "3": { - "1": { - "t": "s", - "si": 31, - "s": 7 - }, - "2": { - "t": "s", - "si": 33, - "s": 0 - } - }, - "4": { - "1": { - "t": "s", - "si": 24, - "s": 8 - }, - "2": { - "t": "n", - "v": 1980, - "s": 9 - } - }, - "5": { - "1": { - "t": "s", - "si": 25, - "s": 10 - }, - "2": { - "t": "n", - "v": 1980, - "s": 9 - } - }, - "6": { - "1": { - "t": "s", - "si": 29, - "s": 11 - }, - "2": { - "t": "n", - "v": 1980, - "s": 9 - } - }, - "7": { - "1": { - "t": "s", - "si": 26, - "s": 8 - }, - "2": { - "t": "n", - "v": 1980, - "s": 9 - } - }, - "8": { - "1": { - "t": "s", - "si": 27, - "s": 10 - }, - "2": { - "t": "n", - "v": 1980, - "s": 9 - } - }, - "9": { - "1": { - "t": "s", - "si": 30, - "s": 11 - }, - "2": { - "t": "n", - "v": 1980, - "s": 9 - } - }, - "10": { - "1": { - "t": "s", - "si": 22, - "s": 8 - }, - "2": { - "t": "n", - "v": 1975, - "s": 9 - } - }, - "11": { - "1": { - "t": "s", - "si": 23, - "s": 10 - }, - "2": { - "t": "n", - "v": 1975, - "s": 9 - } - }, - "12": { - "1": { - "t": "s", - "si": 29, - "s": 11 - }, - "2": { - "t": "n", - "v": 1975, - "s": 9 - } - }, - "13": { - "1": { - "t": "s", - "si": 32, - "s": 8 - }, - "2": { - "t": "n", - "v": 5935, - "s": 9 - } - } - }, - "shared_formulas": [], - "sheet_id": 8, - "state": "visible", - "merge_cells": [], - "comments": [] - }, - { - "dimension": "A1:G5", - "cols": [], - "rows": [], - "name": "shared", - "sheet_data": { - "1": { - "1": { - "t": "n", - "v": 1, - "s": 0 - }, - "2": { - "t": "n", - "v": 5, - "s": 0 - }, - "3": { - "t": "fn", - "f": 0, - "v": 0.8414709848078965, - "s": 0 - }, - "4": { - "t": "fn", - "f": 0, - "v": 0.1682941969615793, - "s": 0 - }, - "5": { - "t": "fn", - "f": 0, - "v": 1, - "s": 0 - } - }, - "2": { - "1": { - "t": "n", - "v": 2, - "s": 0 - }, - "2": { - "t": "n", - "v": 6, - "s": 0 - }, - "3": { - "t": "fn", - "f": 0, - "v": 0.42073549240394825, - "s": 0 - }, - "4": { - "t": "fn", - "f": 0, - "v": 0.1402451641346494, - "s": 0 - }, - "5": { - "t": "fn", - "f": 0, - "v": 2, - "s": 0 - } - }, - "3": { - "1": { - "t": "n", - "v": 3, - "s": 0 - }, - "2": { - "t": "n", - "v": 7, - "s": 0 - }, - "3": { - "t": "fn", - "f": 0, - "v": 0.2804903282692988, - "s": 0 - }, - "4": { - "t": "fn", - "f": 0, - "v": 0.12021014068684235, - "s": 0 - }, - "5": { - "t": "fn", - "f": 0, - "v": 3, - "s": 0 - } - }, - "4": { - "1": { - "t": "n", - "v": 4, - "s": 0 - }, - "2": { - "t": "n", - "v": 8, - "s": 0 - }, - "3": { - "t": "fn", - "f": 0, - "v": 0.21036774620197413, - "s": 0 - }, - "4": { - "t": "fn", - "f": 0, - "v": 0.10518387310098706, - "s": 0 - }, - "5": { - "t": "fn", - "f": 0, - "v": 4, - "s": 0 - } - }, - "5": { - "1": { - "t": "n", - "v": 5, - "s": 0 - }, - "2": { - "t": "n", - "v": 9, - "s": 0 - }, - "3": { - "t": "fn", - "f": 0, - "v": 0.1682941969615793, - "s": 0 - }, - "4": { - "t": "fn", - "f": 0, - "v": 0.09349677608976628, - "s": 0 - }, - "5": { - "t": "fn", - "f": 0, - "v": 5, - "s": 0 - }, - "7": { - "t": "n", - "v": 42, - "s": 0 - } - } - }, - "shared_formulas": ["SIN(R1C1)/R[0]C[-2]"], - "sheet_id": 9, - "state": "visible", - "merge_cells": [], - "comments": [] - }, - { - "dimension": "A1:D4", - "cols": [], - "rows": [], - "name": "Table", - "sheet_data": { - "1": { - "1": { - "t": "s", - "si": 19, - "s": 0 - }, - "2": { - "t": "s", - "si": 20, - "s": 0 - }, - "3": { - "t": "s", - "si": 21, - "s": 0 - }, - "4": { - "t": "s", - "si": 28, - "s": 0 - } - }, - "2": { - "1": { - "t": "s", - "si": 22, - "s": 0 - }, - "2": { - "t": "n", - "v": 1975, - "s": 0 - }, - "3": { - "t": "s", - "si": 23, - "s": 0 - }, - "4": { - "t": "s", - "si": 29, - "s": 0 - } - }, - "3": { - "1": { - "t": "s", - "si": 24, - "s": 0 - }, - "2": { - "t": "n", - "v": 1980, - "s": 0 - }, - "3": { - "t": "s", - "si": 25, - "s": 0 - }, - "4": { - "t": "s", - "si": 29, - "s": 0 - } - }, - "4": { - "1": { - "t": "s", - "si": 26, - "s": 0 - }, - "2": { - "t": "n", - "v": 1980, - "s": 0 - }, - "3": { - "t": "s", - "si": 27, - "s": 0 - }, - "4": { - "t": "s", - "si": 30, - "s": 0 - } - } - }, - "shared_formulas": [], - "sheet_id": 7, - "state": "visible", - "color": "#C55911", - "merge_cells": [], - "comments": [] - }, - { - "dimension": "A1:B3", - "cols": [], - "rows": [], - "name": "Sheet2", - "sheet_data": { - "1": { - "1": { - "t": "n", - "v": 1, - "s": 0 - }, - "2": { - "t": "n", - "v": 5, - "s": 0 - } - }, - "2": { - "1": { - "t": "n", - "v": 2, - "s": 0 - }, - "2": { - "t": "n", - "v": 6, - "s": 0 - } - }, - "3": { - "1": { - "t": "n", - "v": 3, - "s": 0 - }, - "2": { - "t": "n", - "v": 7, - "s": 0 - } - } - }, - "shared_formulas": [], - "sheet_id": 2, - "state": "visible", - "merge_cells": [], - "comments": [] - }, - { - "dimension": "A1:B11", - "cols": [ - { - "min": 1, - "max": 1, - "width": 39.81640625, - "custom_width": true - } - ], - "rows": [ - { - "height": 14.5, - "r": 6, - "custom_format": true, - "custom_height": false, - "s": 15 - }, - { - "height": 14.5, - "r": 7, - "custom_format": true, - "custom_height": false, - "s": 16 - } - ], - "name": "Created fourth", - "sheet_data": { - "1": { - "1": { - "t": "s", - "si": 16, - "s": 0 - } - }, - "4": { - "2": { - "t": "s", - "si": 34, - "s": 0 - } - }, - "6": { - "1": { - "t": "s", - "si": 36, - "s": 15 - } - }, - "7": { - "1": { - "t": "s", - "si": 37, - "s": 16 - } - }, - "11": { - "2": { - "t": "s", - "si": 38, - "s": 15 - } - } - }, - "shared_formulas": [], - "sheet_id": 4, - "state": "visible", - "merge_cells": [], - "comments": [ - { - "text": "Someone else", - "author_name": "", - "cell_ref": "B4" - } - ] - }, - { - "dimension": "A1", - "cols": [], - "rows": [], - "name": "Hidden", - "sheet_data": {}, - "shared_formulas": [], - "sheet_id": 5, - "state": "hidden", - "merge_cells": [], - "comments": [] - } - ], - "styles": { - "num_fmts": [], - "fonts": [ - { - "b": false, - "i": false, - "sz": 11, - "color": "#000000", - "name": "Calibri", - "family": 2, - "scheme": "minor" - }, - { - "b": false, - "i": false, - "sz": 11, - "color": "#FF0000", - "name": "Calibri", - "family": 2, - "scheme": "minor" - }, - { - "b": true, - "i": false, - "sz": 11, - "color": "#000000", - "name": "Calibri", - "family": 2, - "scheme": "minor" - }, - { - "b": false, - "i": false, - "sz": 11, - "color": "#FFC000", - "name": "Calibri", - "family": 2, - "scheme": "minor" - }, - { - "b": true, - "i": false, - "sz": 11, - "color": "#FF0000", - "name": "Calibri", - "family": 2, - "scheme": "minor" - }, - { - "b": false, - "i": true, - "sz": 16, - "color": "#FF0000", - "name": "Calibri", - "family": 2, - "scheme": "minor" - }, - { - "b": false, - "i": false, - "sz": 11, - "color": "#9C5700", - "name": "Calibri", - "family": 2, - "scheme": "minor" - }, - { - "b": false, - "i": false, - "sz": 9, - "color": "#000000", - "name": "Tahoma", - "family": 2, - "scheme": "minor" - }, - { - "b": true, - "i": false, - "sz": 9, - "color": "#000000", - "name": "Tahoma", - "family": 2, - "scheme": "minor" - }, - { - "b": true, - "i": false, - "sz": 16, - "color": "#000000", - "name": "Tahoma", - "family": 2, - "scheme": "minor" - }, - { - "b": false, - "i": false, - "sz": 11, - "color": "#FF66CC", - "name": "Calibri", - "family": 2, - "scheme": "minor" - }, - { - "b": false, - "i": false, - "sz": 11, - "color": "#3F3F76", - "name": "Calibri", - "family": 2, - "scheme": "minor" - }, - { - "b": false, - "i": false, - "sz": 9, - "color": "#000000", - "name": "Tahoma", - "family": 2, - "scheme": "minor" - }, - { - "b": false, - "i": false, - "sz": 11, - "color": "#C55911", - "name": "Calibri", - "family": 2, - "scheme": "minor" - } - ], - "fills": [ - { - "pattern_type": "none" - }, - { - "pattern_type": "gray125" - }, - { - "pattern_type": "solid", - "fg_color": "#FFFF00", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#B5C8E8", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#FFEB9C" - }, - { - "pattern_type": "solid", - "fg_color": "#00B0F0", - "bg_color": "#000000" - }, - { - "pattern_type": "lightUp", - "fg_color": "#DFEBF7" - }, - { - "pattern_type": "solid", - "fg_color": "#FFCC99" - }, - { - "pattern_type": "solid", - "fg_color": "#F2F2F2", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#FFFFFF", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#000000", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#E7E6E6", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#44546A", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#4472C4", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#ED7D31", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#A5A5A5", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#FFC000", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#5B9BD5", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#70AD47", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#808080", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#D0CECE", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#D7DDE5", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#D8E2F3", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#FBE4D5", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#EDEDED", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#FFF2CC", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#DFEBF7", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#E3F0DB", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#C00000", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#FF0000", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#FFC000", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#92D050", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#00B050", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#0070C0", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#002060", - "bg_color": "#000000" - }, - { - "pattern_type": "solid", - "fg_color": "#7030A0", - "bg_color": "#000000" - } - ], - "borders": [ - {}, - { - "left": { - "style": "thick", - "color": "#FFF2CC" - } - }, - { - "left": { - "style": "thick", - "color": "#6C56DC" - }, - "top": { - "style": "thick", - "color": "#6C56DC" - }, - "bottom": { - "style": "thick", - "color": "#6C56DC" - }, - "diagonal": { - "style": "thick", - "color": "#F8C9AB" - } - }, - { - "left": { - "style": "thin", - "color": "#7F7F7F" - }, - "right": { - "style": "thin", - "color": "#7F7F7F" - }, - "top": { - "style": "thin", - "color": "#7F7F7F" - }, - "bottom": { - "style": "thin", - "color": "#7F7F7F" - } - }, - { - "left": { - "style": "thin", - "color": "#7F7F7F" - }, - "right": { - "style": "thin", - "color": "#7F7F7F" - }, - "bottom": { - "style": "thin", - "color": "#7F7F7F" - } - }, - { - "left": { - "style": "thin", - "color": "#7F7F7F" - }, - "right": { - "style": "thin", - "color": "#7F7F7F" - }, - "top": { - "style": "thin", - "color": "#7F7F7F" - } - }, - { - "left": { - "style": "medium", - "color": "#000000" - }, - "top": { - "style": "medium", - "color": "#000000" - } - }, - { - "top": { - "style": "medium", - "color": "#000000" - } - }, - { - "right": { - "style": "medium", - "color": "#000000" - }, - "top": { - "style": "medium", - "color": "#000000" - } - }, - { - "left": { - "style": "medium", - "color": "#000000" - } - }, - { - "right": { - "style": "medium", - "color": "#000000" - } - }, - { - "left": { - "style": "medium", - "color": "#000000" - }, - "bottom": { - "style": "medium", - "color": "#000000" - } - }, - { - "bottom": { - "style": "medium", - "color": "#000000" - } - }, - { - "right": { - "style": "medium", - "color": "#000000" - }, - "bottom": { - "style": "medium", - "color": "#000000" - } - } - ], - "cell_style_xfs": [ - { - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 0, - "border_id": 0 - }, - { - "num_fmt_id": 0, - "font_id": 6, - "fill_id": 4, - "border_id": 0, - "apply_number_format": false, - "apply_border": false, - "apply_alignment": false, - "apply_protection": false - }, - { - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 3, - "apply_number_format": false, - "apply_alignment": false, - "apply_protection": false - }, - { - "num_fmt_id": 0, - "font_id": 13, - "fill_id": 8, - "border_id": 0 - } - ], - "cell_xfs": [ - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 0, - "border_id": 0 - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 1, - "fill_id": 2, - "border_id": 0, - "apply_font": true, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 2, - "fill_id": 0, - "border_id": 0, - "apply_font": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 3, - "fill_id": 3, - "border_id": 0, - "apply_font": true, - "apply_fill": true - }, - { - "xf_id": 1, - "num_fmt_id": 0, - "font_id": 6, - "fill_id": 4, - "border_id": 0 - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 10, - "fill_id": 5, - "border_id": 2, - "apply_border": true, - "apply_font": true, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 6, - "border_id": 1, - "apply_border": true, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 0, - "border_id": 0 - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 0, - "border_id": 0, - "alignment": { - "horizontal": "left" - }, - "apply_alignment": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 0, - "border_id": 0, - "apply_number_format": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 0, - "border_id": 0, - "alignment": { - "horizontal": "left" - }, - "apply_alignment": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 0, - "border_id": 0, - "alignment": { - "horizontal": "left" - }, - "apply_alignment": true - }, - { - "xf_id": 2, - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 3 - }, - { - "xf_id": 2, - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 4, - "apply_border": true - }, - { - "xf_id": 2, - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 5, - "apply_border": true - }, - { - "xf_id": 3, - "num_fmt_id": 0, - "font_id": 13, - "fill_id": 8, - "border_id": 0, - "read_only": true - }, - { - "xf_id": 3, - "num_fmt_id": 0, - "font_id": 13, - "fill_id": 2, - "border_id": 0, - "read_only": true, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 0, - "border_id": 0, - "alignment": { - "horizontal": "center" - }, - "apply_alignment": true - }, - { - "xf_id": 2, - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 6, - "alignment": { - "horizontal": "center" - }, - "apply_border": true, - "apply_alignment": true - }, - { - "xf_id": 2, - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 7, - "alignment": { - "horizontal": "center" - }, - "apply_border": true, - "apply_alignment": true - }, - { - "xf_id": 2, - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 8, - "alignment": { - "horizontal": "center" - }, - "apply_border": true, - "apply_alignment": true - }, - { - "xf_id": 2, - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 9, - "alignment": { - "horizontal": "center" - }, - "apply_border": true, - "apply_alignment": true - }, - { - "xf_id": 2, - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 0, - "alignment": { - "horizontal": "center" - }, - "apply_border": true, - "apply_alignment": true - }, - { - "xf_id": 2, - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 10, - "alignment": { - "horizontal": "center" - }, - "apply_border": true, - "apply_alignment": true - }, - { - "xf_id": 2, - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 11, - "alignment": { - "horizontal": "center" - }, - "apply_border": true, - "apply_alignment": true - }, - { - "xf_id": 2, - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 12, - "alignment": { - "horizontal": "center" - }, - "apply_border": true, - "apply_alignment": true - }, - { - "xf_id": 2, - "num_fmt_id": 0, - "font_id": 11, - "fill_id": 7, - "border_id": 13, - "alignment": { - "horizontal": "center" - }, - "apply_border": true, - "apply_alignment": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 9, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 10, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 11, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 12, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 13, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 14, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 15, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 16, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 17, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 18, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 8, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 19, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 20, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 21, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 22, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 23, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 24, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 25, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 26, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 27, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 28, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 29, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 30, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 2, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 31, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 32, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 5, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 33, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 34, - "border_id": 0, - "apply_fill": true - }, - { - "xf_id": 0, - "num_fmt_id": 0, - "font_id": 0, - "fill_id": 35, - "border_id": 0, - "apply_fill": true - } - ], - "cell_styles": [ - { - "name": "Input", - "xf_id": 2, - "builtin_id": 20 - }, - { - "name": "Neutral", - "xf_id": 1, - "builtin_id": 28 - }, - { - "name": "Normal", - "xf_id": 0, - "builtin_id": 0 - }, - { - "name": "ReadOnly1", - "xf_id": 3, - "builtin_id": 0 - } - ] - }, - "name": "example" -} diff --git a/xlsx/tests/test.rs b/xlsx/tests/test.rs index 358f6ff..255acb3 100644 --- a/xlsx/tests/test.rs +++ b/xlsx/tests/test.rs @@ -3,33 +3,32 @@ use uuid::Uuid; use ironcalc::compare::{test_file, test_load_and_saving}; use ironcalc::export::save_to_xlsx; -use ironcalc::import::{load_from_excel, load_model_from_xlsx}; -use ironcalc_base::types::{HorizontalAlignment, VerticalAlignment, Workbook}; +use ironcalc::import::{load_from_icalc, load_from_xlsx}; +use ironcalc_base::types::{HorizontalAlignment, VerticalAlignment}; use ironcalc_base::Model; // This is a functional test. // We check that the output of example.xlsx is what we expect. #[test] fn test_example() { - let model = load_from_excel("tests/example.xlsx", "en", "UTC").unwrap(); - assert_eq!(model.worksheets[0].frozen_rows, 0); - assert_eq!(model.worksheets[0].frozen_columns, 0); - let contents = - fs::read_to_string("tests/example.json").expect("Something went wrong reading the file"); - let model2: Workbook = serde_json::from_str(&contents).unwrap(); - let s = serde_json::to_string(&model).unwrap(); - assert_eq!(model, model2, "{s}"); + let model = load_from_xlsx("tests/example.xlsx", "en", "UTC").unwrap(); + let workbook = model.workbook; + assert_eq!(workbook.worksheets[0].frozen_rows, 0); + assert_eq!(workbook.worksheets[0].frozen_columns, 0); + let model2 = load_from_icalc("tests/example.ic").unwrap(); + let s = bitcode::encode(&model2.workbook); + assert_eq!(workbook, model2.workbook, "{:?}", s); } #[test] fn test_save_to_xlsx() { - let mut model = load_model_from_xlsx("tests/example.xlsx", "en", "UTC").unwrap(); + let mut model = load_from_xlsx("tests/example.xlsx", "en", "UTC").unwrap(); model.evaluate(); let temp_file_name = "temp_file_example.xlsx"; // test can safe save_to_xlsx(&model, temp_file_name).unwrap(); // test can open - let model = load_model_from_xlsx(temp_file_name, "en", "UTC").unwrap(); + let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap(); let metadata = &model.workbook.metadata; assert_eq!(metadata.application, "IronCalc Sheets"); // FIXME: This will need to be updated once we fix versioning @@ -41,7 +40,9 @@ fn test_save_to_xlsx() { #[test] fn test_freeze() { // freeze has 3 frozen columns and 2 frozen rows - let model = load_from_excel("tests/freeze.xlsx", "en", "UTC").unwrap(); + let model = load_from_xlsx("tests/freeze.xlsx", "en", "UTC") + .unwrap() + .workbook; assert_eq!(model.worksheets[0].frozen_rows, 2); assert_eq!(model.worksheets[0].frozen_columns, 3); } @@ -49,7 +50,9 @@ fn test_freeze() { #[test] fn test_split() { // We test that a workbook with split panes do not produce frozen rows and columns - let model = load_from_excel("tests/split.xlsx", "en", "UTC").unwrap(); + let model = load_from_xlsx("tests/split.xlsx", "en", "UTC") + .unwrap() + .workbook; assert_eq!(model.worksheets[0].frozen_rows, 0); assert_eq!(model.worksheets[0].frozen_columns, 0); } @@ -145,14 +148,14 @@ fn test_model_has_correct_styles(model: &Model) { #[test] fn test_simple_text() { - let model = load_model_from_xlsx("tests/basic_text.xlsx", "en", "UTC").unwrap(); + let model = load_from_xlsx("tests/basic_text.xlsx", "en", "UTC").unwrap(); test_model_has_correct_styles(&model); let temp_file_name = "temp_file_test_named_styles.xlsx"; save_to_xlsx(&model, temp_file_name).unwrap(); - let model = load_model_from_xlsx(temp_file_name, "en", "UTC").unwrap(); + let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap(); fs::remove_file(temp_file_name).unwrap(); test_model_has_correct_styles(&model); } @@ -160,7 +163,9 @@ fn test_simple_text() { #[test] fn test_defined_names_casing() { let test_file_path = "tests/calc_tests/defined_names_for_unit_test.xlsx"; - let loaded_workbook = load_from_excel(test_file_path, "en", "UTC").unwrap(); + let loaded_workbook = load_from_xlsx(test_file_path, "en", "UTC") + .unwrap() + .workbook; let mut model = Model::from_bytes(&bitcode::encode(&loaded_workbook)).unwrap(); let (row, column) = (2, 13); // B13