Compare commits

...

2 Commits

Author SHA1 Message Date
Nicolás Hatcher
afecf29356 UPDATE: Adds bincode to serializer/deserializer 2024-03-14 01:20:50 +01:00
Nicolás Hatcher Andrés
1381533b9c Update README.md 2024-03-12 20:42:06 +01:00
8 changed files with 134 additions and 80 deletions

27
Cargo.lock generated
View File

@@ -38,6 +38,25 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bincode"
version = "2.0.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95"
dependencies = [
"bincode_derive",
"serde",
]
[[package]]
name = "bincode_derive"
version = "2.0.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e30759b3b99a1b802a7a3aa21c85c3ded5c28e1c83170d82d70f08bbf7f3e4c"
dependencies = [
"virtue",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.14.0" version = "3.14.0"
@@ -191,6 +210,7 @@ dependencies = [
name = "ironcalc" name = "ironcalc"
version = "0.1.3" version = "0.1.3"
dependencies = [ dependencies = [
"bincode",
"chrono", "chrono",
"ironcalc_base", "ironcalc_base",
"itertools", "itertools",
@@ -206,6 +226,7 @@ dependencies = [
name = "ironcalc_base" name = "ironcalc_base"
version = "0.1.2" version = "0.1.2"
dependencies = [ dependencies = [
"bincode",
"chrono", "chrono",
"chrono-tz", "chrono-tz",
"js-sys", "js-sys",
@@ -541,6 +562,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "virtue"
version = "0.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.10.0+wasi-snapshot-preview1" version = "0.10.0+wasi-snapshot-preview1"

View File

@@ -117,55 +117,7 @@ See more examples in the `examples` folder of the xlsx crate.
# ROADMAP # ROADMAP
> [!WARNING] See https://github.com/ironcalc
> This is work-in-progress. IronCalc in developed in the open. Expect things to be broken and change quickly until version 0.5
Major milestones:
* MVP, version 0.5.0: We intend to have a working version by mid March 2024 (version 0.5, MVP)
* Stable, version 1.0.0 will come later in December 2024
MVP stands for _Minimum Viable Product_
## Version 0.5 or MVP (early 2024)
Version 0.5 includes the engine, javascript and nodejs bindings and a web application
Features of the engine include:
* Read and write xlsx files
* API to set and read values from cells
* Implemented 192 Excel functions
* Time functions with timezones
* Prepared for i18n but will only support English
* Wide test coverage
UI features of the web application (backed by the engine):
* Enter values and formulas. Browse mode
* Italics, bold, underline, horizontal alignment
* Number formatting
* Add/remove/rename sheets
* Copy/Paste extend values
* Keyboard navigation
* Delete/Add rows and columns
* Resize rows and columns
* Correct scrolling and navigation
## Version 1.0 or Stable (December 2024)
Minor milestones in the ROADMAD for version 1.0.0 (engine and UI):
* Implementation of arrays and array formulas
* Formula documentation and context help
* Merge cells
* Pivot tables
* Define name manager (mostly UI)
* Update main evaluation algorithm with a support graph
* Dynamic arrays (SORT, UNIQUE, ..)
* Full i18n support with different locales and languages
* Python bindings
* Full test coverage
# Early testing # Early testing
@@ -192,4 +144,4 @@ Licensed under either of
* [MIT license](LICENSE-MIT) * [MIT license](LICENSE-MIT)
* [Apache license, version 2.0](LICENSE-Apache-2.0) * [Apache license, version 2.0](LICENSE-Apache-2.0)
at your option. at your option.

View File

@@ -19,6 +19,7 @@ chrono = "0.4"
chrono-tz = "0.7.0" chrono-tz = "0.7.0"
regex = "1.0" regex = "1.0"
once_cell = "1.16.0" once_cell = "1.16.0"
bincode = "=2.0.0-rc.3"
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = { version = "0.3.60" } js-sys = { version = "0.3.60" }

View File

@@ -1,5 +1,6 @@
use std::fmt; use std::fmt;
use bincode::{Decode, Encode};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_repr::{Deserialize_repr, Serialize_repr};
@@ -80,7 +81,7 @@ impl fmt::Display for OpProduct {
/// * "#ERROR!" means there was an error processing the formula (for instance "=A1+") /// * "#ERROR!" means there was an error processing the formula (for instance "=A1+")
/// * "#N/IMPL!" means the formula or feature in Excel but has not been implemented in IronCalc /// * "#N/IMPL!" means the formula or feature in Excel but has not been implemented in IronCalc
/// Note that they are serialized/deserialized by index /// Note that they are serialized/deserialized by index
#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Eq, Clone)] #[derive(Serialize_repr, Deserialize_repr, Decode, Encode, Debug, PartialEq, Eq, Clone)]
#[repr(u8)] #[repr(u8)]
pub enum Error { pub enum Error {
REF, REF,

View File

@@ -7,6 +7,7 @@
//! IronCalc is row first. A cell is referenced by (`sheet`, `row`, `column`) //! IronCalc is row first. A cell is referenced by (`sheet`, `row`, `column`)
//! //!
use bincode::config;
use serde_json::json; use serde_json::json;
use std::collections::HashMap; use std::collections::HashMap;
@@ -1718,6 +1719,12 @@ impl Model {
} }
} }
/// bin
pub fn to_binary_str(&self) -> Vec<u8> {
let config = config::standard();
bincode::encode_to_vec(&self.workbook, config).expect("")
}
/// 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 sheet_markup(&self, sheet: u32) -> Result<String, String> {
let worksheet = self.workbook.worksheet(sheet)?; let worksheet = self.workbook.worksheet(sheet)?;

View File

@@ -1,3 +1,4 @@
use bincode::{Decode, Encode};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt::Display}; use std::{collections::HashMap, fmt::Display};
@@ -33,7 +34,7 @@ fn hashmap_is_empty(h: &HashMap<String, Table>) -> bool {
h.values().len() == 0 h.values().len() == 0
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct Metadata { pub struct Metadata {
pub application: String, pub application: String,
pub app_version: String, pub app_version: String,
@@ -43,13 +44,13 @@ pub struct Metadata {
pub last_modified: String, //"2020-11-20T16:24:35" pub last_modified: String, //"2020-11-20T16:24:35"
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct WorkbookSettings { pub struct WorkbookSettings {
pub tz: String, pub tz: String,
pub locale: String, pub locale: String,
} }
/// An internal representation of an IronCalc Workbook /// An internal representation of an IronCalc Workbook
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Clone)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Workbook { pub struct Workbook {
pub shared_strings: Vec<String>, pub shared_strings: Vec<String>,
@@ -65,7 +66,7 @@ pub struct Workbook {
} }
/// A defined name. The `sheet_id` is the sheet index in case the name is local /// A defined name. The `sheet_id` is the sheet index in case the name is local
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct DefinedName { pub struct DefinedName {
pub name: String, pub name: String,
pub formula: String, pub formula: String,
@@ -79,7 +80,7 @@ pub struct DefinedName {
/// * state: /// * state:
/// 18.18.68 ST_SheetState (Sheet Visibility Types) /// 18.18.68 ST_SheetState (Sheet Visibility Types)
/// hidden, veryHidden, visible /// hidden, veryHidden, visible
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum SheetState { pub enum SheetState {
Visible, Visible,
@@ -98,7 +99,7 @@ impl Display for SheetState {
} }
/// Internal representation of a worksheet Excel object /// Internal representation of a worksheet Excel object
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Clone)]
pub struct Worksheet { pub struct Worksheet {
pub dimension: String, pub dimension: String,
pub cols: Vec<Col>, pub cols: Vec<Col>,
@@ -125,7 +126,7 @@ pub struct Worksheet {
pub type SheetData = HashMap<i32, HashMap<i32, Cell>>; pub type SheetData = HashMap<i32, HashMap<i32, Cell>>;
// ECMA-376-1:2016 section 18.3.1.73 // ECMA-376-1:2016 section 18.3.1.73
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Clone)]
pub struct Row { pub struct Row {
/// Row index /// Row index
pub r: i32, pub r: i32,
@@ -139,7 +140,7 @@ pub struct Row {
} }
// ECMA-376-1:2016 section 18.3.1.13 // ECMA-376-1:2016 section 18.3.1.13
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Clone)]
pub struct Col { pub struct Col {
// Column definitions are defined on ranges, unlike rows which store unique, per-row entries. // 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. /// First column affected by this record. Settings apply to column in \[min, max\] range.
@@ -164,7 +165,7 @@ pub enum CellType {
CompoundData = 128, CompoundData = 128,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, Clone, PartialEq)]
#[serde(tag = "t", deny_unknown_fields)] #[serde(tag = "t", deny_unknown_fields)]
pub enum Cell { pub enum Cell {
#[serde(rename = "empty")] #[serde(rename = "empty")]
@@ -208,7 +209,7 @@ impl Default for Cell {
} }
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct Comment { pub struct Comment {
pub text: String, pub text: String,
pub author_name: String, pub author_name: String,
@@ -218,7 +219,7 @@ pub struct Comment {
} }
// ECMA-376-1:2016 section 18.5.1.2 // ECMA-376-1:2016 section 18.5.1.2
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct Table { pub struct Table {
pub name: String, pub name: String,
pub display_name: String, pub display_name: String,
@@ -241,7 +242,7 @@ pub struct Table {
// totals_row_label vs totals_row_function might be mutually exclusive. Use an enum? // totals_row_label vs totals_row_function might be mutually exclusive. Use an enum?
// the totals_row_function is an enum not String methinks // the totals_row_function is an enum not String methinks
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct TableColumn { pub struct TableColumn {
pub id: u32, pub id: u32,
pub name: String, pub name: String,
@@ -271,7 +272,7 @@ impl Default for TableColumn {
} }
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone, Default)]
pub struct TableStyleInfo { pub struct TableStyleInfo {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>, pub name: Option<String>,
@@ -289,7 +290,7 @@ pub struct TableStyleInfo {
pub show_column_stripes: bool, pub show_column_stripes: bool,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct Styles { pub struct Styles {
pub num_fmts: Vec<NumFmt>, pub num_fmts: Vec<NumFmt>,
pub fonts: Vec<Font>, pub fonts: Vec<Font>,
@@ -314,7 +315,7 @@ impl Default for Styles {
} }
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct Style { pub struct Style {
pub alignment: Option<Alignment>, pub alignment: Option<Alignment>,
pub num_fmt: String, pub num_fmt: String,
@@ -324,7 +325,7 @@ pub struct Style {
pub quote_prefix: bool, pub quote_prefix: bool,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct NumFmt { pub struct NumFmt {
pub num_fmt_id: i32, pub num_fmt_id: i32,
pub format_code: String, pub format_code: String,
@@ -342,7 +343,7 @@ impl Default for NumFmt {
// ST_FontScheme simple type (§18.18.33). // ST_FontScheme simple type (§18.18.33).
// Usually major fonts are used for styles like headings, // Usually major fonts are used for styles like headings,
// and minor fonts are used for body and paragraph text. // and minor fonts are used for body and paragraph text.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
#[derive(Default)] #[derive(Default)]
pub enum FontScheme { pub enum FontScheme {
@@ -362,7 +363,7 @@ impl Display for FontScheme {
} }
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct Font { pub struct Font {
#[serde(default = "default_as_false")] #[serde(default = "default_as_false")]
#[serde(skip_serializing_if = "is_false")] #[serde(skip_serializing_if = "is_false")]
@@ -405,7 +406,7 @@ impl Default for Font {
} }
// TODO: Maybe use an enum for the pattern_type values here? // TODO: Maybe use an enum for the pattern_type values here?
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct Fill { pub struct Fill {
pub pattern_type: String, pub pattern_type: String,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
@@ -424,7 +425,7 @@ impl Default for Fill {
} }
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum HorizontalAlignment { pub enum HorizontalAlignment {
Center, Center,
@@ -466,7 +467,7 @@ impl Display for HorizontalAlignment {
} }
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum VerticalAlignment { pub enum VerticalAlignment {
Bottom, Bottom,
@@ -501,7 +502,7 @@ impl Display for VerticalAlignment {
} }
// 1762 // 1762
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone, Default)]
pub struct Alignment { pub struct Alignment {
#[serde(default)] #[serde(default)]
#[serde(skip_serializing_if = "HorizontalAlignment::is_default")] #[serde(skip_serializing_if = "HorizontalAlignment::is_default")]
@@ -514,7 +515,7 @@ pub struct Alignment {
pub wrap_text: bool, pub wrap_text: bool,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct CellStyleXfs { pub struct CellStyleXfs {
pub num_fmt_id: i32, pub num_fmt_id: i32,
pub font_id: i32, pub font_id: i32,
@@ -557,7 +558,7 @@ impl Default for CellStyleXfs {
} }
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone, Default)]
pub struct CellXfs { pub struct CellXfs {
pub xf_id: i32, pub xf_id: i32,
pub num_fmt_id: i32, pub num_fmt_id: i32,
@@ -589,7 +590,7 @@ pub struct CellXfs {
pub alignment: Option<Alignment>, pub alignment: Option<Alignment>,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct CellStyles { pub struct CellStyles {
pub name: String, pub name: String,
pub xf_id: i32, pub xf_id: i32,
@@ -606,7 +607,7 @@ impl Default for CellStyles {
} }
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum BorderStyle { pub enum BorderStyle {
Thin, Thin,
@@ -636,13 +637,13 @@ impl Display for BorderStyle {
} }
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)]
pub struct BorderItem { pub struct BorderItem {
pub style: BorderStyle, pub style: BorderStyle,
pub color: Option<String>, pub color: Option<String>,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone, Default)]
pub struct Border { pub struct Border {
#[serde(default = "default_as_false")] #[serde(default = "default_as_false")]
#[serde(skip_serializing_if = "is_false")] #[serde(skip_serializing_if = "is_false")]
@@ -664,7 +665,7 @@ pub struct Border {
/// Information need to show a sheet tab in the UI /// Information need to show a sheet tab in the UI
/// The color is serialized only if it is not Color::None /// The color is serialized only if it is not Color::None
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq)]
pub struct SheetInfo { pub struct SheetInfo {
pub name: String, pub name: String,
pub state: String, pub state: String,

View File

@@ -23,6 +23,7 @@ thiserror = "1.0"
ironcalc_base = { path = "../base", version = "0.1.0" } ironcalc_base = { path = "../base", version = "0.1.0" }
itertools = "0.10.5" itertools = "0.10.5"
chrono = "0.4" chrono = "0.4"
bincode = "=2.0.0-rc.3"
[dev-dependencies] [dev-dependencies]
uuid = { version = "1.2.2", features = ["serde", "v4"] } uuid = { version = "1.2.2", features = ["serde", "v4"] }
@@ -34,3 +35,8 @@ path = "src/lib.rs"
[[bin]] [[bin]]
name = "test" name = "test"
path = "src/bin/test.rs" path = "src/bin/test.rs"
[[bin]]
name = "bench"
path = "src/bin/bench.rs"

59
xlsx/src/bin/bench.rs Normal file
View File

@@ -0,0 +1,59 @@
use std::{fs, io::Write, time::Instant};
use bincode::config;
use ironcalc::import::load_model_from_xlsx;
use ironcalc_base::{types::Workbook, model::Model};
fn main() {
let args: Vec<_> = std::env::args().collect();
if args.len() != 2 {
panic!("Usage: {} <file.xlsx>", args[0]);
}
let file_name = &args[1];
let now = Instant::now();
let mut model = load_model_from_xlsx(file_name, "en", "UTC").unwrap();
let elapsed_time = now.elapsed();
println!("Loaded model from xlsx: {:?}", elapsed_time);
model.evaluate();
let now = Instant::now();
let s = model.to_json_str();
let elapsed_time = now.elapsed();
println!("Stringify json: {:?}", elapsed_time);
{
let now = Instant::now();
let decoded : Workbook = serde_json::from_str(&s).unwrap();
let elapsed_time = now.elapsed();
println!("Parse from json: {:?} and name {}", elapsed_time, decoded.name);
}
let file_name_json = format!("{}.json", file_name);
let file_path = std::path::Path::new(&file_name_json);
let mut file = fs::File::create(file_path).unwrap();
file.write_all(s.as_bytes()).unwrap();
let now = Instant::now();
let s = model.to_binary_str();
let elapsed_time = now.elapsed();
println!("stringify to binary: {:?}", elapsed_time);
{
let config = config::standard();
let now = Instant::now();
let (decoded, _): (Workbook, usize) = bincode::decode_from_slice(&s[..], config).unwrap();
let elapsed_time = now.elapsed();
println!("Parse from binary: {:?} and {}", elapsed_time, decoded.name);
}
let file_name_binary = format!("{}.binary", file_name);
let file_path = std::path::Path::new(&file_name_binary);
let mut file = fs::File::create(file_path).unwrap();
file.write_all(&s).unwrap();
{
let config = config::standard();
let now = Instant::now();
let s = &fs::read(file_name_binary).unwrap();
let (decoded, _): (Workbook, usize) = bincode::decode_from_slice(&s[..], config).unwrap();
let model = Model::from_workbook(decoded).unwrap();
let elapsed_time = now.elapsed();
println!("Loaded from binary file: {:?} and {}", elapsed_time, model.workbook.name);
}
}