use bitcode::{Decode, Encode}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt::Display}; use crate::expressions::token::Error; fn default_as_false() -> bool { false } fn is_false(b: &bool) -> bool { !*b } #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct Metadata { pub application: String, pub app_version: String, pub creator: String, pub last_modified_by: String, pub created: String, // "2020-08-06T21:20:53Z", pub last_modified: String, //"2020-11-20T16:24:35" } #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct WorkbookSettings { pub tz: String, pub locale: String, } /// An internal representation of an IronCalc Workbook #[derive(Encode, Decode, Debug, PartialEq, Clone)] pub struct Workbook { pub shared_strings: Vec, pub defined_names: Vec, pub worksheets: Vec, pub styles: Styles, pub name: String, pub settings: WorkbookSettings, pub metadata: Metadata, pub tables: HashMap, } /// A defined name. The `sheet_id` is the sheet index in case the name is local #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct DefinedName { pub name: String, pub formula: String, pub sheet_id: Option, } // TODO: Move to worksheet.rs make frozen_rows/columns private and u32 /// Internal representation of a worksheet Excel object /// * state: /// 18.18.68 ST_SheetState (Sheet Visibility Types) /// hidden, veryHidden, visible #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub enum SheetState { Visible, Hidden, VeryHidden, } impl Display for SheetState { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { match self { SheetState::Visible => write!(formatter, "visible"), SheetState::Hidden => write!(formatter, "hidden"), SheetState::VeryHidden => write!(formatter, "veryHidden"), } } } #[derive(Encode, Decode, Debug, PartialEq, Clone)] pub struct Selection { pub is_selected: bool, pub row: i32, pub column: i32, pub range: [i32; 4], } /// Internal representation of a worksheet Excel object #[derive(Encode, Decode, Debug, PartialEq, Clone)] pub struct Worksheet { pub dimension: String, pub cols: Vec, pub rows: Vec, pub name: String, pub sheet_data: SheetData, pub shared_formulas: Vec, pub sheet_id: u32, pub state: SheetState, pub color: Option, pub merge_cells: Vec, pub comments: Vec, pub frozen_rows: i32, pub frozen_columns: i32, pub selection: Selection, } /// Internal representation of Excel's sheet_data /// It is row first and because of this all of our API's should be row first pub type SheetData = HashMap>; // ECMA-376-1:2016 section 18.3.1.73 #[derive(Encode, Decode, Debug, PartialEq, Clone)] pub struct Row { /// Row index pub r: i32, pub height: f64, pub custom_format: bool, pub custom_height: bool, pub s: i32, pub hidden: bool, } // ECMA-376-1:2016 section 18.3.1.13 #[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, pub style: Option, } /// Cell type enum matching Excel TYPE() function values. #[derive(Debug, Eq, PartialEq)] pub enum CellType { Number = 1, Text = 2, LogicalValue = 4, ErrorValue = 16, Array = 64, CompoundData = 128, } #[derive(Encode, Decode, Debug, Clone, PartialEq)] pub enum Cell { 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 ErrorCell { ei: Error, s: i32, }, // Always a shared string SharedString { si: i32, s: i32, }, // Non evaluated Formula CellFormula { f: i32, s: i32, }, CellFormulaBoolean { f: i32, v: bool, s: i32, }, CellFormulaNumber { f: i32, v: f64, s: i32, }, // always inline string CellFormulaString { f: i32, v: String, s: i32, }, CellFormulaError { f: i32, ei: Error, s: i32, // Origin: Sheet3!C4 o: String, // Error Message: "Not implemented function" m: String, }, // TODO: Array formulas } impl Default for Cell { fn default() -> Self { Cell::EmptyCell { s: 0 } } } #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct Comment { pub text: String, pub author_name: String, pub author_id: Option, pub cell_ref: String, } // ECMA-376-1:2016 section 18.5.1.2 #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct Table { pub name: String, pub display_name: String, pub sheet_name: String, pub reference: String, pub totals_row_count: u32, pub header_row_count: u32, pub header_row_dxf_id: Option, pub data_dxf_id: Option, pub totals_row_dxf_id: Option, pub columns: Vec, pub style_info: TableStyleInfo, 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(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct TableColumn { pub id: u32, pub name: String, pub totals_row_label: Option, pub header_row_dxf_id: Option, pub data_dxf_id: Option, pub totals_row_dxf_id: Option, pub totals_row_function: Option, } impl Default for TableColumn { fn default() -> Self { TableColumn { id: 0, name: "Column".to_string(), totals_row_label: None, totals_row_function: None, data_dxf_id: None, header_row_dxf_id: None, totals_row_dxf_id: None, } } } #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Default)] pub struct TableStyleInfo { pub name: Option, pub show_first_column: bool, pub show_last_column: bool, pub show_row_stripes: bool, pub show_column_stripes: bool, } #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct Styles { pub num_fmts: Vec, pub fonts: Vec, pub fills: Vec, pub borders: Vec, pub cell_style_xfs: Vec, pub cell_xfs: Vec, pub cell_styles: Vec, } impl Default for Styles { fn default() -> Self { Styles { num_fmts: vec![], fonts: vec![Default::default()], fills: vec![Default::default()], borders: vec![Default::default()], cell_style_xfs: vec![Default::default()], cell_xfs: vec![Default::default()], cell_styles: vec![Default::default()], } } } #[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct Style { #[serde(skip_serializing_if = "Option::is_none")] pub alignment: Option, pub num_fmt: String, pub fill: Fill, pub font: Font, pub border: Border, pub quote_prefix: bool, } #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct NumFmt { pub num_fmt_id: i32, pub format_code: String, } impl Default for NumFmt { fn default() -> Self { NumFmt { num_fmt_id: 0, format_code: "general".to_string(), } } } // ST_FontScheme simple type (ยง18.18.33). // Usually major fonts are used for styles like headings, // and minor fonts are used for body and paragraph text. #[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] #[serde(rename_all = "lowercase")] #[derive(Default)] pub enum FontScheme { #[default] Minor, Major, None, } impl Display for FontScheme { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { match self { FontScheme::Minor => write!(formatter, "minor"), FontScheme::Major => write!(formatter, "major"), FontScheme::None => write!(formatter, "none"), } } } #[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct Font { #[serde(default = "default_as_false")] #[serde(skip_serializing_if = "is_false")] pub strike: bool, #[serde(default = "default_as_false")] #[serde(skip_serializing_if = "is_false")] pub u: bool, // seems that Excel supports a bit more - double underline / account underline etc. #[serde(default = "default_as_false")] #[serde(skip_serializing_if = "is_false")] pub b: bool, #[serde(default = "default_as_false")] #[serde(skip_serializing_if = "is_false")] pub i: bool, pub sz: i32, pub color: Option, pub name: String, // This is the font family fallback // 1 -> serif // 2 -> sans serif // 3 -> monospaced // ... pub family: i32, pub scheme: FontScheme, } impl Default for Font { fn default() -> Self { Font { strike: false, u: false, b: false, i: false, sz: 11, color: Some("#000000".to_string()), name: "Calibri".to_string(), family: 2, scheme: FontScheme::Minor, } } } // TODO: Maybe use an enum for the pattern_type values here? #[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct Fill { pub pattern_type: String, #[serde(skip_serializing_if = "Option::is_none")] pub fg_color: Option, #[serde(skip_serializing_if = "Option::is_none")] pub bg_color: Option, } impl Default for Fill { fn default() -> Self { Fill { pattern_type: "none".to_string(), fg_color: Default::default(), bg_color: Default::default(), } } } #[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] #[serde(rename_all = "lowercase")] pub enum HorizontalAlignment { Center, CenterContinuous, Distributed, Fill, General, Justify, Left, Right, } // Note that alignment in "General" depends on type impl Default for HorizontalAlignment { fn default() -> Self { Self::General } } impl HorizontalAlignment { fn is_default(&self) -> bool { self == &HorizontalAlignment::default() } } // FIXME: Is there a way to generate this automatically? impl Display for HorizontalAlignment { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { match self { HorizontalAlignment::Center => write!(formatter, "center"), HorizontalAlignment::CenterContinuous => write!(formatter, "centerContinuous"), HorizontalAlignment::Distributed => write!(formatter, "distributed"), HorizontalAlignment::Fill => write!(formatter, "fill"), HorizontalAlignment::General => write!(formatter, "general"), HorizontalAlignment::Justify => write!(formatter, "justify"), HorizontalAlignment::Left => write!(formatter, "left"), HorizontalAlignment::Right => write!(formatter, "right"), } } } #[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] #[serde(rename_all = "lowercase")] pub enum VerticalAlignment { Bottom, Center, Distributed, Justify, Top, } impl VerticalAlignment { fn is_default(&self) -> bool { self == &VerticalAlignment::default() } } impl Default for VerticalAlignment { fn default() -> Self { Self::Bottom } } impl Display for VerticalAlignment { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { match self { VerticalAlignment::Bottom => write!(formatter, "bottom"), VerticalAlignment::Center => write!(formatter, "center"), VerticalAlignment::Distributed => write!(formatter, "distributed"), VerticalAlignment::Justify => write!(formatter, "justify"), VerticalAlignment::Top => write!(formatter, "top"), } } } // 1762 #[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, Default)] pub struct Alignment { #[serde(default)] #[serde(skip_serializing_if = "HorizontalAlignment::is_default")] pub horizontal: HorizontalAlignment, #[serde(skip_serializing_if = "VerticalAlignment::is_default")] #[serde(default)] pub vertical: VerticalAlignment, #[serde(default = "default_as_false")] #[serde(skip_serializing_if = "is_false")] pub wrap_text: bool, } #[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, pub apply_number_format: bool, pub apply_border: bool, pub apply_alignment: bool, pub apply_protection: bool, pub apply_font: bool, pub apply_fill: bool, } impl Default for CellStyleXfs { fn default() -> Self { CellStyleXfs { num_fmt_id: 0, font_id: 0, fill_id: 0, border_id: 0, apply_number_format: true, apply_border: true, apply_alignment: true, apply_protection: true, apply_font: true, apply_fill: true, } } } #[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, pub apply_number_format: bool, pub apply_border: bool, pub apply_alignment: bool, pub apply_protection: bool, pub apply_font: bool, pub apply_fill: bool, pub quote_prefix: bool, pub alignment: Option, } #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct CellStyles { pub name: String, pub xf_id: i32, pub builtin_id: i32, } impl Default for CellStyles { fn default() -> Self { CellStyles { name: "normal".to_string(), xf_id: 0, builtin_id: 0, } } } #[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] #[serde(rename_all = "lowercase")] pub enum BorderStyle { Thin, Medium, Thick, Double, Dotted, SlantDashDot, MediumDashed, MediumDashDotDot, MediumDashDot, } impl Display for BorderStyle { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { match self { BorderStyle::Thin => write!(formatter, "thin"), BorderStyle::Thick => write!(formatter, "thick"), BorderStyle::SlantDashDot => write!(formatter, "slantdashdot"), BorderStyle::MediumDashed => write!(formatter, "mediumdashed"), BorderStyle::MediumDashDotDot => write!(formatter, "mediumdashdotdot"), BorderStyle::MediumDashDot => write!(formatter, "mediumdashdot"), BorderStyle::Medium => write!(formatter, "medium"), BorderStyle::Double => write!(formatter, "double"), BorderStyle::Dotted => write!(formatter, "dotted"), } } } #[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct BorderItem { pub style: BorderStyle, pub color: Option, } #[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, Default)] pub struct Border { #[serde(default = "default_as_false")] #[serde(skip_serializing_if = "is_false")] pub diagonal_up: bool, #[serde(default = "default_as_false")] #[serde(skip_serializing_if = "is_false")] pub diagonal_down: bool, #[serde(skip_serializing_if = "Option::is_none")] pub left: Option, #[serde(skip_serializing_if = "Option::is_none")] pub right: Option, #[serde(skip_serializing_if = "Option::is_none")] pub top: Option, #[serde(skip_serializing_if = "Option::is_none")] pub bottom: Option, #[serde(skip_serializing_if = "Option::is_none")] pub diagonal: Option, } /// Information need to show a sheet tab in the UI /// The color is serialized only if it is not Color::None #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct SheetProperties { pub name: String, pub state: String, pub sheet_id: u32, #[serde(skip_serializing_if = "Option::is_none")] pub color: Option, }