commit f041daa050c0ee8a1492a36070b48abc55c96a31 Author: nhatcher Date: Wed Feb 7 19:11:37 2024 +0000 deploy: 07c8e1af816f55ffd442c38abd9bf2717bb4cf25 diff --git a/.lock b/.lock new file mode 100644 index 0000000..e69de29 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/crates.js b/crates.js new file mode 100644 index 0000000..8bfa295 --- /dev/null +++ b/crates.js @@ -0,0 +1 @@ +window.ALL_CRATES = ["ironcalc","ironcalc_base","test"]; \ No newline at end of file diff --git a/help.html b/help.html new file mode 100644 index 0000000..c90b74c --- /dev/null +++ b/help.html @@ -0,0 +1 @@ +Help

Rustdoc help

Back
\ No newline at end of file diff --git a/ironcalc/all.html b/ironcalc/all.html new file mode 100644 index 0000000..d513d16 --- /dev/null +++ b/ironcalc/all.html @@ -0,0 +1 @@ +List of all items in this crate
\ No newline at end of file diff --git a/ironcalc/compare/fn.compare.html b/ironcalc/compare/fn.compare.html new file mode 100644 index 0000000..14cbc31 --- /dev/null +++ b/ironcalc/compare/fn.compare.html @@ -0,0 +1,5 @@ +compare in ironcalc::compare - Rust

Function ironcalc::compare::compare

source ·
pub fn compare(
+    model1: &Model,
+    model2: &Model
+) -> Result<Vec<Diff>, CompareError>
Expand description

Compares two Models in the internal representation and returns a list of differences

+
\ No newline at end of file diff --git a/ironcalc/compare/fn.test_file.html b/ironcalc/compare/fn.test_file.html new file mode 100644 index 0000000..64d3398 --- /dev/null +++ b/ironcalc/compare/fn.test_file.html @@ -0,0 +1,2 @@ +test_file in ironcalc::compare - Rust

Function ironcalc::compare::test_file

source ·
pub fn test_file(file_path: &str) -> Result<(), String>
Expand description

Tests that file in file_path produces the same results in Excel and in IronCalc.

+
\ No newline at end of file diff --git a/ironcalc/compare/fn.test_load_and_saving.html b/ironcalc/compare/fn.test_load_and_saving.html new file mode 100644 index 0000000..be71314 --- /dev/null +++ b/ironcalc/compare/fn.test_load_and_saving.html @@ -0,0 +1,5 @@ +test_load_and_saving in ironcalc::compare - Rust
pub fn test_load_and_saving(
+    file_path: &str,
+    temp_dir_name: &Path
+) -> Result<(), String>
Expand description

Tests that file in file_path can be converted to xlsx and read again

+
\ No newline at end of file diff --git a/ironcalc/compare/index.html b/ironcalc/compare/index.html new file mode 100644 index 0000000..91d567e --- /dev/null +++ b/ironcalc/compare/index.html @@ -0,0 +1 @@ +ironcalc::compare - Rust

Module ironcalc::compare

source ·

Structs

Functions

  • Compares two Models in the internal representation and returns a list of differences
  • Tests that file in file_path produces the same results in Excel and in IronCalc.
  • Tests that file in file_path can be converted to xlsx and read again
\ No newline at end of file diff --git a/ironcalc/compare/sidebar-items.js b/ironcalc/compare/sidebar-items.js new file mode 100644 index 0000000..41be48a --- /dev/null +++ b/ironcalc/compare/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["compare","test_file","test_load_and_saving"],"struct":["CompareError","Diff"]}; \ No newline at end of file diff --git a/ironcalc/compare/struct.CompareError.html b/ironcalc/compare/struct.CompareError.html new file mode 100644 index 0000000..3338d2a --- /dev/null +++ b/ironcalc/compare/struct.CompareError.html @@ -0,0 +1,12 @@ +CompareError in ironcalc::compare - Rust
pub struct CompareError { /* private fields */ }

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc/compare/struct.Diff.html b/ironcalc/compare/struct.Diff.html new file mode 100644 index 0000000..333811e --- /dev/null +++ b/ironcalc/compare/struct.Diff.html @@ -0,0 +1,19 @@ +Diff in ironcalc::compare - Rust

Struct ironcalc::compare::Diff

source ·
pub struct Diff {
+    pub sheet_name: String,
+    pub row: i32,
+    pub column: i32,
+    pub value1: Cell,
+    pub value2: Cell,
+    pub reason: String,
+}

Fields§

§sheet_name: String§row: i32§column: i32§value1: Cell§value2: Cell§reason: String

Auto Trait Implementations§

§

impl RefUnwindSafe for Diff

§

impl Send for Diff

§

impl Sync for Diff

§

impl Unpin for Diff

§

impl UnwindSafe for Diff

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc/error/enum.XlsxError.html b/ironcalc/error/enum.XlsxError.html new file mode 100644 index 0000000..879ab52 --- /dev/null +++ b/ironcalc/error/enum.XlsxError.html @@ -0,0 +1,23 @@ +XlsxError in ironcalc::error - Rust
pub enum XlsxError {
+    IO(String),
+    Zip(String),
+    Xml(String),
+    Workbook(String),
+    Evaluation(Vec<String>),
+    Comparison(String),
+    NotImplemented(String),
+}

Variants§

§

IO(String)

§

Zip(String)

§

Xml(String)

§

Workbook(String)

§

Evaluation(Vec<String>)

§

Comparison(String)

§

NotImplemented(String)

Implementations§

Trait Implementations§

source§

impl Debug for XlsxError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for XlsxError

source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for XlsxError

1.30.0 · source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl From<Error> for XlsxError

source§

fn from(error: Error) -> Self

Converts to this type from the input type.
source§

impl From<Error> for XlsxError

source§

fn from(error: Error) -> Self

Converts to this type from the input type.
source§

impl From<ParseFloatError> for XlsxError

source§

fn from(error: ParseFloatError) -> Self

Converts to this type from the input type.
source§

impl From<ParseIntError> for XlsxError

source§

fn from(error: ParseIntError) -> Self

Converts to this type from the input type.
source§

impl From<ZipError> for XlsxError

source§

fn from(error: ZipError) -> Self

Converts to this type from the input type.
source§

impl PartialEq for XlsxError

source§

fn eq(&self, other: &XlsxError) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for XlsxError

source§

impl StructuralEq for XlsxError

source§

impl StructuralPartialEq for XlsxError

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc/error/index.html b/ironcalc/error/index.html new file mode 100644 index 0000000..e8f7ced --- /dev/null +++ b/ironcalc/error/index.html @@ -0,0 +1 @@ +ironcalc::error - Rust
\ No newline at end of file diff --git a/ironcalc/error/sidebar-items.js b/ironcalc/error/sidebar-items.js new file mode 100644 index 0000000..1ee0184 --- /dev/null +++ b/ironcalc/error/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["XlsxError"]}; \ No newline at end of file diff --git a/ironcalc/export/fn.save_to_json.html b/ironcalc/export/fn.save_to_json.html new file mode 100644 index 0000000..4d4c29d --- /dev/null +++ b/ironcalc/export/fn.save_to_json.html @@ -0,0 +1,2 @@ +save_to_json in ironcalc::export - Rust

Function ironcalc::export::save_to_json

source ·
pub fn save_to_json(workbook: Workbook, output: &str)
Expand description

Exports an internal representation of a workbook into an equivalent IronCalc json format

+
\ No newline at end of file diff --git a/ironcalc/export/fn.save_to_xlsx.html b/ironcalc/export/fn.save_to_xlsx.html new file mode 100644 index 0000000..1a823f9 --- /dev/null +++ b/ironcalc/export/fn.save_to_xlsx.html @@ -0,0 +1,2 @@ +save_to_xlsx in ironcalc::export - Rust

Function ironcalc::export::save_to_xlsx

source ·
pub fn save_to_xlsx(model: &Model, file_name: &str) -> Result<(), XlsxError>
Expand description

Exports a model to an xlsx file

+
\ No newline at end of file diff --git a/ironcalc/export/fn.save_xlsx_to_writer.html b/ironcalc/export/fn.save_xlsx_to_writer.html new file mode 100644 index 0000000..5500fd9 --- /dev/null +++ b/ironcalc/export/fn.save_xlsx_to_writer.html @@ -0,0 +1,4 @@ +save_xlsx_to_writer in ironcalc::export - Rust
pub fn save_xlsx_to_writer<W: Write + Seek>(
+    model: &Model,
+    writer: W
+) -> Result<W, XlsxError>
\ No newline at end of file diff --git a/ironcalc/export/index.html b/ironcalc/export/index.html new file mode 100644 index 0000000..bbc2999 --- /dev/null +++ b/ironcalc/export/index.html @@ -0,0 +1 @@ +ironcalc::export - Rust

Module ironcalc::export

source ·

Functions

\ No newline at end of file diff --git a/ironcalc/export/sidebar-items.js b/ironcalc/export/sidebar-items.js new file mode 100644 index 0000000..5a470b9 --- /dev/null +++ b/ironcalc/export/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["save_to_json","save_to_xlsx","save_xlsx_to_writer"]}; \ No newline at end of file diff --git a/ironcalc/import/fn.load_from_excel.html b/ironcalc/import/fn.load_from_excel.html new file mode 100644 index 0000000..3d0e025 --- /dev/null +++ b/ironcalc/import/fn.load_from_excel.html @@ -0,0 +1,6 @@ +load_from_excel in ironcalc::import - Rust
pub fn load_from_excel(
+    file_name: &str,
+    locale: &str,
+    tz: &str
+) -> Result<Workbook, XlsxError>
Expand description

Imports a file from disk into an internal representation

+
\ No newline at end of file diff --git a/ironcalc/import/fn.load_model_from_xlsx.html b/ironcalc/import/fn.load_model_from_xlsx.html new file mode 100644 index 0000000..0141d41 --- /dev/null +++ b/ironcalc/import/fn.load_model_from_xlsx.html @@ -0,0 +1,5 @@ +load_model_from_xlsx in ironcalc::import - Rust
pub fn load_model_from_xlsx(
+    file_name: &str,
+    locale: &str,
+    tz: &str
+) -> Result<Model, XlsxError>
\ No newline at end of file diff --git a/ironcalc/import/index.html b/ironcalc/import/index.html new file mode 100644 index 0000000..586dc10 --- /dev/null +++ b/ironcalc/import/index.html @@ -0,0 +1 @@ +ironcalc::import - Rust

Module ironcalc::import

source ·

Functions

\ No newline at end of file diff --git a/ironcalc/import/sidebar-items.js b/ironcalc/import/sidebar-items.js new file mode 100644 index 0000000..008a259 --- /dev/null +++ b/ironcalc/import/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["load_from_excel","load_model_from_xlsx"]}; \ No newline at end of file diff --git a/ironcalc/index.html b/ironcalc/index.html new file mode 100644 index 0000000..f8acf5e --- /dev/null +++ b/ironcalc/index.html @@ -0,0 +1,47 @@ +ironcalc - Rust

Crate ironcalc

source ·
Expand description

This crate reads an xlsx file and transforms it into an internal representation (Model). +An xlsx is a zip file containing a set of folders and xml files. The IronCalc json structure mimics the relevant parts of the Excel zip. +Although the xlsx structure is quite complicated, it’s essentials regarding the spreadsheet technology are easier to grasp.

+

The simplest workbook folder structure might look like this:

+
docProps
+    app.xml
+    core.xml
+
+_rels
+    .rels
+
+xl
+    _rels
+        workbook.xml.rels
+    theme
+        theme1.xml
+    worksheets
+        sheet1.xml
+    calcChain.xml
+    styles.xml
+    workbook.xml
+    sharedStrings.xml
+
+[Content_Types].xml
+
+

Note that more complicated workbooks will have many more files and folders. +For instance charts, pivot tables, comments, tables,…

+

The relevant json structure in IronCalc will be:

+
{
+    "name": "Workbook1",
+    "defined_names": [],
+    "shared_strings": [],
+    "worksheets": [],
+    "styles": {
+        "num_fmts": [],
+        "fonts": [],
+        "fills": [],
+        "borders": [],
+        "cell_style_xfs": [],
+        "cell_styles" : [],
+        "cell_xfs": []
+    }
+}
+
+

Note that there is not a 1-1 correspondence but there is a close resemblance.

+

Re-exports

Modules

\ No newline at end of file diff --git a/ironcalc/sidebar-items.js b/ironcalc/sidebar-items.js new file mode 100644 index 0000000..849b7e1 --- /dev/null +++ b/ironcalc/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["compare","error","export","import"]}; \ No newline at end of file diff --git a/ironcalc_base/all.html b/ironcalc_base/all.html new file mode 100644 index 0000000..e9feb78 --- /dev/null +++ b/ironcalc_base/all.html @@ -0,0 +1 @@ +List of all items in this crate

List of all items

Structs

Enums

Functions

Type Aliases

Constants

\ No newline at end of file diff --git a/ironcalc_base/calc_result/index.html b/ironcalc_base/calc_result/index.html new file mode 100644 index 0000000..79f9408 --- /dev/null +++ b/ironcalc_base/calc_result/index.html @@ -0,0 +1 @@ +ironcalc_base::calc_result - Rust
\ No newline at end of file diff --git a/ironcalc_base/calc_result/sidebar-items.js b/ironcalc_base/calc_result/sidebar-items.js new file mode 100644 index 0000000..e53162b --- /dev/null +++ b/ironcalc_base/calc_result/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["CellReference","Range"]}; \ No newline at end of file diff --git a/ironcalc_base/calc_result/struct.CellReference.html b/ironcalc_base/calc_result/struct.CellReference.html new file mode 100644 index 0000000..75bbae7 --- /dev/null +++ b/ironcalc_base/calc_result/struct.CellReference.html @@ -0,0 +1,19 @@ +CellReference in ironcalc_base::calc_result - Rust
pub struct CellReference {
+    pub sheet: u32,
+    pub column: i32,
+    pub row: i32,
+}

Fields§

§sheet: u32§column: i32§row: i32

Trait Implementations§

source§

impl Clone for CellReference

source§

fn clone(&self) -> CellReference

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CellReference

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for CellReference

source§

fn eq(&self, other: &CellReference) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Copy for CellReference

source§

impl Eq for CellReference

source§

impl StructuralEq for CellReference

source§

impl StructuralPartialEq for CellReference

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/calc_result/struct.Range.html b/ironcalc_base/calc_result/struct.Range.html new file mode 100644 index 0000000..8e45fa4 --- /dev/null +++ b/ironcalc_base/calc_result/struct.Range.html @@ -0,0 +1,16 @@ +Range in ironcalc_base::calc_result - Rust
pub struct Range {
+    pub left: CellReference,
+    pub right: CellReference,
+}

Fields§

§left: CellReference§right: CellReference

Trait Implementations§

source§

impl Clone for Range

source§

fn clone(&self) -> Range

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Range

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl RefUnwindSafe for Range

§

impl Send for Range

§

impl Sync for Range

§

impl Unpin for Range

§

impl UnwindSafe for Range

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/cell/enum.CellValue.html b/ironcalc_base/cell/enum.CellValue.html new file mode 100644 index 0000000..7224b6c --- /dev/null +++ b/ironcalc_base/cell/enum.CellValue.html @@ -0,0 +1,23 @@ +CellValue in ironcalc_base::cell - Rust
pub enum CellValue {
+    None,
+    String(String),
+    Number(f64),
+    Boolean(bool),
+}
Expand description

A CellValue is the representation of the cell content.

+

Variants§

§

None

§

String(String)

§

Number(f64)

§

Boolean(bool)

Implementations§

Trait Implementations§

source§

impl Debug for CellValue

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for CellValue

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl From<&str> for CellValue

source§

fn from(value: &str) -> Self

Converts to this type from the input type.
source§

impl From<String> for CellValue

source§

fn from(value: String) -> Self

Converts to this type from the input type.
source§

impl From<bool> for CellValue

source§

fn from(value: bool) -> Self

Converts to this type from the input type.
source§

impl From<f64> for CellValue

source§

fn from(value: f64) -> Self

Converts to this type from the input type.
source§

impl PartialEq for CellValue

source§

fn eq(&self, other: &CellValue) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for CellValue

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl StructuralPartialEq for CellValue

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/cell/index.html b/ironcalc_base/cell/index.html new file mode 100644 index 0000000..c26c06b --- /dev/null +++ b/ironcalc_base/cell/index.html @@ -0,0 +1 @@ +ironcalc_base::cell - Rust

Module ironcalc_base::cell

source ·

Enums

  • A CellValue is the representation of the cell content.
\ No newline at end of file diff --git a/ironcalc_base/cell/sidebar-items.js b/ironcalc_base/cell/sidebar-items.js new file mode 100644 index 0000000..f900625 --- /dev/null +++ b/ironcalc_base/cell/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["CellValue"]}; \ No newline at end of file diff --git a/ironcalc_base/expressions/index.html b/ironcalc_base/expressions/index.html new file mode 100644 index 0000000..120d29d --- /dev/null +++ b/ironcalc_base/expressions/index.html @@ -0,0 +1 @@ +ironcalc_base::expressions - Rust

Modules

\ No newline at end of file diff --git a/ironcalc_base/expressions/lexer/enum.LexerMode.html b/ironcalc_base/expressions/lexer/enum.LexerMode.html new file mode 100644 index 0000000..6bdb949 --- /dev/null +++ b/ironcalc_base/expressions/lexer/enum.LexerMode.html @@ -0,0 +1,18 @@ +LexerMode in ironcalc_base::expressions::lexer - Rust
pub enum LexerMode {
+    A1,
+    R1C1,
+}

Variants§

§

A1

§

R1C1

Trait Implementations§

source§

impl Clone for LexerMode

source§

fn clone(&self) -> LexerMode

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl PartialEq for LexerMode

source§

fn eq(&self, other: &LexerMode) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for LexerMode

source§

impl StructuralEq for LexerMode

source§

impl StructuralPartialEq for LexerMode

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/lexer/index.html b/ironcalc_base/expressions/lexer/index.html new file mode 100644 index 0000000..5ca1cee --- /dev/null +++ b/ironcalc_base/expressions/lexer/index.html @@ -0,0 +1,33 @@ +ironcalc_base::expressions::lexer - Rust
Expand description

A tokenizer for spreadsheet formulas.

+

This is meant to feed a formula parser.

+

You will need to instantiate it with a language and a locale.

+

It supports two working modes:

+
    +
  1. A1 or display mode +This is for user formulas. References are like D4, D$4 or F5:T10
  2. +
  3. R1C1, internal or runtime mode +A reference like R1C1 refers to $A$1 and R3C4 to $D$4 +R[2]C[5] refers to a cell two rows below and five columns to the right +It uses the ‘en’ locale and language. +This is used internally at runtime.
  4. +
+

Formulas look different in different locales:

+

=IF(A1, B1, NA()) versus =IF(A1; B1; NA())

+

Also numbers are different:

+

1,123.45 versus 1.123,45

+

The names of the errors and functions are different in different languages, +but they stay the same in different locales.

+

Note that in IronCalc if you are using a locale different from ‘en’ or a language different from ‘en’ +you will still need the ‘en’ locale and language because formulas are stored in that language and locale

+

Examples:

+
use ironcalc_base::expressions::lexer::{Lexer, LexerMode};
+use ironcalc_base::expressions::token::{TokenType, OpCompare};
+use ironcalc_base::locale::get_locale;
+use ironcalc_base::language::get_language;
+
+let locale = get_locale("en").unwrap();
+let language = get_language("en").unwrap();
+let mut lexer = Lexer::new("=A1*SUM(Sheet2!C3:D5)", LexerMode::A1, &locale, &language);
+assert_eq!(lexer.next_token(), TokenType::Compare(OpCompare::Equal));
+assert!(matches!(lexer.next_token(), TokenType::Reference { .. }));
+

Modules

Structs

Enums

\ No newline at end of file diff --git a/ironcalc_base/expressions/lexer/sidebar-items.js b/ironcalc_base/expressions/lexer/sidebar-items.js new file mode 100644 index 0000000..e85f40c --- /dev/null +++ b/ironcalc_base/expressions/lexer/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["LexerMode"],"mod":["util"],"struct":["Lexer","LexerError"]}; \ No newline at end of file diff --git a/ironcalc_base/expressions/lexer/struct.Lexer.html b/ironcalc_base/expressions/lexer/struct.Lexer.html new file mode 100644 index 0000000..09d3285 --- /dev/null +++ b/ironcalc_base/expressions/lexer/struct.Lexer.html @@ -0,0 +1,31 @@ +Lexer in ironcalc_base::expressions::lexer - Rust
pub struct Lexer { /* private fields */ }
Expand description

Tokenize an input

+

Implementations§

source§

impl Lexer

source

pub fn new( + formula: &str, + mode: LexerMode, + locale: &Locale, + language: &Language +) -> Lexer

Creates a new Lexer that returns the tokens of a formula.

+
source

pub fn set_lexer_mode(&mut self, mode: LexerMode)

Changes the lexer mode

+
source

pub fn is_a1_mode(&self) -> bool

Returns true if mode is A1

+
source

pub fn get_formula(&self) -> String

Returns the formula

+
source

pub fn get_position(&self) -> i32

Returns the position of the lexer

+
source

pub fn set_formula(&mut self, content: &str)

Resets the formula

+
source

pub fn expect(&mut self, tk: TokenType) -> Result<(), LexerError>

Returns an error if the token is not the expected one.

+
source

pub fn peek_token(&mut self) -> TokenType

Checks the next token without advancing position +See also advance_token

+
source

pub fn advance_token(&mut self)

Advances position. This is used in conjunction with peek_token +It is a noop if the has not been a previous peek_token

+
source

pub fn next_token(&mut self) -> TokenType

Returns the next token

+

Trait Implementations§

source§

impl Clone for Lexer

source§

fn clone(&self) -> Lexer

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

§

impl RefUnwindSafe for Lexer

§

impl Send for Lexer

§

impl Sync for Lexer

§

impl Unpin for Lexer

§

impl UnwindSafe for Lexer

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/lexer/struct.LexerError.html b/ironcalc_base/expressions/lexer/struct.LexerError.html new file mode 100644 index 0000000..5cdead5 --- /dev/null +++ b/ironcalc_base/expressions/lexer/struct.LexerError.html @@ -0,0 +1,18 @@ +LexerError in ironcalc_base::expressions::lexer - Rust
pub struct LexerError {
+    pub position: usize,
+    pub message: String,
+}

Fields§

§position: usize§message: String

Trait Implementations§

source§

impl Clone for LexerError

source§

fn clone(&self) -> LexerError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for LexerError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for LexerError

source§

fn eq(&self, other: &LexerError) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for LexerError

source§

impl StructuralEq for LexerError

source§

impl StructuralPartialEq for LexerError

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/lexer/util/fn.get_tokens.html b/ironcalc_base/expressions/lexer/util/fn.get_tokens.html new file mode 100644 index 0000000..65d9a43 --- /dev/null +++ b/ironcalc_base/expressions/lexer/util/fn.get_tokens.html @@ -0,0 +1,31 @@ +get_tokens in ironcalc_base::expressions::lexer::util - Rust
pub fn get_tokens(formula: &str) -> Vec<MarkedToken>
Expand description

Returns a list of marked tokens for a formula

+

Examples

+
use ironcalc_base::expressions::{
+     lexer::util::{get_tokens, MarkedToken},
+     token::{OpSum, TokenType},
+};
+
+let marked_tokens = get_tokens("A1+1");
+let first_t = MarkedToken {
+    token: TokenType::Reference {
+        sheet: None,
+        row: 1,
+        column: 1,
+        absolute_column: false,
+        absolute_row: false,
+    },
+    start: 0,
+    end: 2,
+};
+let second_t = MarkedToken {
+    token: TokenType::Addition(OpSum::Add),
+    start:2,
+    end: 3
+};
+let third_t = MarkedToken {
+    token: TokenType::Number(1.0),
+    start:3,
+    end: 4
+};
+assert_eq!(marked_tokens, vec![first_t, second_t, third_t]);
+
\ No newline at end of file diff --git a/ironcalc_base/expressions/lexer/util/index.html b/ironcalc_base/expressions/lexer/util/index.html new file mode 100644 index 0000000..bc75c0e --- /dev/null +++ b/ironcalc_base/expressions/lexer/util/index.html @@ -0,0 +1 @@ +ironcalc_base::expressions::lexer::util - Rust

Structs

  • A MarkedToken is a token together with its position on a formula

Functions

  • Returns a list of marked tokens for a formula
\ No newline at end of file diff --git a/ironcalc_base/expressions/lexer/util/sidebar-items.js b/ironcalc_base/expressions/lexer/util/sidebar-items.js new file mode 100644 index 0000000..3be9a12 --- /dev/null +++ b/ironcalc_base/expressions/lexer/util/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["get_tokens"],"struct":["MarkedToken"]}; \ No newline at end of file diff --git a/ironcalc_base/expressions/lexer/util/struct.MarkedToken.html b/ironcalc_base/expressions/lexer/util/struct.MarkedToken.html new file mode 100644 index 0000000..2c9c4b4 --- /dev/null +++ b/ironcalc_base/expressions/lexer/util/struct.MarkedToken.html @@ -0,0 +1,20 @@ +MarkedToken in ironcalc_base::expressions::lexer::util - Rust
pub struct MarkedToken {
+    pub token: TokenType,
+    pub start: i32,
+    pub end: i32,
+}
Expand description

A MarkedToken is a token together with its position on a formula

+

Fields§

§token: TokenType§start: i32§end: i32

Trait Implementations§

source§

impl Debug for MarkedToken

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for MarkedToken

source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for MarkedToken

source§

fn eq(&self, other: &MarkedToken) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl StructuralPartialEq for MarkedToken

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/parser/enum.Node.html b/ironcalc_base/expressions/parser/enum.Node.html new file mode 100644 index 0000000..3d6a2ad --- /dev/null +++ b/ironcalc_base/expressions/parser/enum.Node.html @@ -0,0 +1,105 @@ +Node in ironcalc_base::expressions::parser - Rust
pub enum Node {
+
Show 21 variants BooleanKind(bool), + NumberKind(f64), + StringKind(String), + ReferenceKind { + sheet_name: Option<String>, + sheet_index: u32, + absolute_row: bool, + absolute_column: bool, + row: i32, + column: i32, + }, + RangeKind { + sheet_name: Option<String>, + sheet_index: u32, + absolute_row1: bool, + absolute_column1: bool, + row1: i32, + column1: i32, + absolute_row2: bool, + absolute_column2: bool, + row2: i32, + column2: i32, + }, + WrongReferenceKind { + sheet_name: Option<String>, + absolute_row: bool, + absolute_column: bool, + row: i32, + column: i32, + }, + WrongRangeKind { + sheet_name: Option<String>, + absolute_row1: bool, + absolute_column1: bool, + row1: i32, + column1: i32, + absolute_row2: bool, + absolute_column2: bool, + row2: i32, + column2: i32, + }, + OpRangeKind { + left: Box<Node>, + right: Box<Node>, + }, + OpConcatenateKind { + left: Box<Node>, + right: Box<Node>, + }, + OpSumKind { + kind: OpSum, + left: Box<Node>, + right: Box<Node>, + }, + OpProductKind { + kind: OpProduct, + left: Box<Node>, + right: Box<Node>, + }, + OpPowerKind { + left: Box<Node>, + right: Box<Node>, + }, + FunctionKind { + kind: Function, + args: Vec<Node>, + }, + InvalidFunctionKind { + name: String, + args: Vec<Node>, + }, + ArrayKind(Vec<Node>), + VariableKind(String), + CompareKind { + kind: OpCompare, + left: Box<Node>, + right: Box<Node>, + }, + UnaryKind { + kind: OpUnary, + right: Box<Node>, + }, + ErrorKind(Error), + ParseErrorKind { + formula: String, + message: String, + position: usize, + }, + EmptyArgKind, +
}

Variants§

§

BooleanKind(bool)

§

NumberKind(f64)

§

StringKind(String)

§

ReferenceKind

Fields

§sheet_name: Option<String>
§sheet_index: u32
§absolute_row: bool
§absolute_column: bool
§row: i32
§column: i32
§

RangeKind

Fields

§sheet_name: Option<String>
§sheet_index: u32
§absolute_row1: bool
§absolute_column1: bool
§row1: i32
§column1: i32
§absolute_row2: bool
§absolute_column2: bool
§row2: i32
§column2: i32
§

WrongReferenceKind

Fields

§sheet_name: Option<String>
§absolute_row: bool
§absolute_column: bool
§row: i32
§column: i32
§

WrongRangeKind

Fields

§sheet_name: Option<String>
§absolute_row1: bool
§absolute_column1: bool
§row1: i32
§column1: i32
§absolute_row2: bool
§absolute_column2: bool
§row2: i32
§column2: i32
§

OpRangeKind

Fields

§left: Box<Node>
§right: Box<Node>
§

OpConcatenateKind

Fields

§left: Box<Node>
§right: Box<Node>
§

OpSumKind

Fields

§kind: OpSum
§left: Box<Node>
§right: Box<Node>
§

OpProductKind

Fields

§left: Box<Node>
§right: Box<Node>
§

OpPowerKind

Fields

§left: Box<Node>
§right: Box<Node>
§

FunctionKind

Fields

§kind: Function
§args: Vec<Node>
§

InvalidFunctionKind

Fields

§name: String
§args: Vec<Node>
§

ArrayKind(Vec<Node>)

§

VariableKind(String)

§

CompareKind

Fields

§left: Box<Node>
§right: Box<Node>
§

UnaryKind

Fields

§kind: OpUnary
§right: Box<Node>
§

ErrorKind(Error)

§

ParseErrorKind

Fields

§formula: String
§message: String
§position: usize
§

EmptyArgKind

Trait Implementations§

source§

impl Clone for Node

source§

fn clone(&self) -> Node

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Node

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Node

source§

fn eq(&self, other: &Node) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl StructuralPartialEq for Node

Auto Trait Implementations§

§

impl RefUnwindSafe for Node

§

impl Send for Node

§

impl Sync for Node

§

impl Unpin for Node

§

impl UnwindSafe for Node

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/parser/index.html b/ironcalc_base/expressions/parser/index.html new file mode 100644 index 0000000..5ee2184 --- /dev/null +++ b/ironcalc_base/expressions/parser/index.html @@ -0,0 +1,24 @@ +ironcalc_base::expressions::parser - Rust
Expand description

GRAMAR

+opComp   => '=' | '<' | '>' | '<=' } '>=' | '<>'
+opFactor => '*' | '/'
+unaryOp  => '-' | '+'
+
+expr    => concat (opComp concat)*
+concat  => term ('&' term)*
+term    => factor (opFactor factor)*
+factor  => prod (opProd prod)*
+prod    => power ('^' power)*
+power   => (unaryOp)* range '%'*
+range   => primary (':' primary)?
+primary => '(' expr ')'
+        => number
+        => function '(' f_args ')'
+        => name
+        => string
+        => '{' a_args '}'
+        => bool
+        => bool()
+        => error
+
+f_args  => e (',' e)*
+

Modules

Structs

Enums

\ No newline at end of file diff --git a/ironcalc_base/expressions/parser/move_formula/index.html b/ironcalc_base/expressions/parser/move_formula/index.html new file mode 100644 index 0000000..0922ffa --- /dev/null +++ b/ironcalc_base/expressions/parser/move_formula/index.html @@ -0,0 +1 @@ +ironcalc_base::expressions::parser::move_formula - Rust
\ No newline at end of file diff --git a/ironcalc_base/expressions/parser/move_formula/sidebar-items.js b/ironcalc_base/expressions/parser/move_formula/sidebar-items.js new file mode 100644 index 0000000..5244ce0 --- /dev/null +++ b/ironcalc_base/expressions/parser/move_formula/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {}; \ No newline at end of file diff --git a/ironcalc_base/expressions/parser/sidebar-items.js b/ironcalc_base/expressions/parser/sidebar-items.js new file mode 100644 index 0000000..20f78fe --- /dev/null +++ b/ironcalc_base/expressions/parser/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Node"],"mod":["move_formula","stringify","walk"],"struct":["Parser"]}; \ No newline at end of file diff --git a/ironcalc_base/expressions/parser/stringify/enum.DisplaceData.html b/ironcalc_base/expressions/parser/stringify/enum.DisplaceData.html new file mode 100644 index 0000000..b5cde9a --- /dev/null +++ b/ironcalc_base/expressions/parser/stringify/enum.DisplaceData.html @@ -0,0 +1,41 @@ +DisplaceData in ironcalc_base::expressions::parser::stringify - Rust
pub enum DisplaceData {
+    Column {
+        sheet: u32,
+        column: i32,
+        delta: i32,
+    },
+    Row {
+        sheet: u32,
+        row: i32,
+        delta: i32,
+    },
+    CellHorizontal {
+        sheet: u32,
+        row: i32,
+        column: i32,
+        delta: i32,
+    },
+    CellVertical {
+        sheet: u32,
+        row: i32,
+        column: i32,
+        delta: i32,
+    },
+    ColumnMove {
+        sheet: u32,
+        column: i32,
+        delta: i32,
+    },
+    None,
+}

Variants§

§

Column

Fields

§sheet: u32
§column: i32
§delta: i32
§

Row

Fields

§sheet: u32
§row: i32
§delta: i32
§

CellHorizontal

Fields

§sheet: u32
§row: i32
§column: i32
§delta: i32
§

CellVertical

Fields

§sheet: u32
§row: i32
§column: i32
§delta: i32
§

ColumnMove

Fields

§sheet: u32
§column: i32
§delta: i32
§

None

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/parser/stringify/fn.to_excel_string.html b/ironcalc_base/expressions/parser/stringify/fn.to_excel_string.html new file mode 100644 index 0000000..4d68efd --- /dev/null +++ b/ironcalc_base/expressions/parser/stringify/fn.to_excel_string.html @@ -0,0 +1 @@ +to_excel_string in ironcalc_base::expressions::parser::stringify - Rust
pub fn to_excel_string(node: &Node, context: &CellReferenceRC) -> String
\ No newline at end of file diff --git a/ironcalc_base/expressions/parser/stringify/fn.to_rc_format.html b/ironcalc_base/expressions/parser/stringify/fn.to_rc_format.html new file mode 100644 index 0000000..79bce00 --- /dev/null +++ b/ironcalc_base/expressions/parser/stringify/fn.to_rc_format.html @@ -0,0 +1 @@ +to_rc_format in ironcalc_base::expressions::parser::stringify - Rust
pub fn to_rc_format(node: &Node) -> String
\ No newline at end of file diff --git a/ironcalc_base/expressions/parser/stringify/fn.to_string.html b/ironcalc_base/expressions/parser/stringify/fn.to_string.html new file mode 100644 index 0000000..7da57e6 --- /dev/null +++ b/ironcalc_base/expressions/parser/stringify/fn.to_string.html @@ -0,0 +1 @@ +to_string in ironcalc_base::expressions::parser::stringify - Rust
pub fn to_string(node: &Node, context: &CellReferenceRC) -> String
\ No newline at end of file diff --git a/ironcalc_base/expressions/parser/stringify/fn.to_string_displaced.html b/ironcalc_base/expressions/parser/stringify/fn.to_string_displaced.html new file mode 100644 index 0000000..16b6fca --- /dev/null +++ b/ironcalc_base/expressions/parser/stringify/fn.to_string_displaced.html @@ -0,0 +1,5 @@ +to_string_displaced in ironcalc_base::expressions::parser::stringify - Rust
pub fn to_string_displaced(
+    node: &Node,
+    context: &CellReferenceRC,
+    displace_data: &DisplaceData
+) -> String
\ No newline at end of file diff --git a/ironcalc_base/expressions/parser/stringify/index.html b/ironcalc_base/expressions/parser/stringify/index.html new file mode 100644 index 0000000..368eaf0 --- /dev/null +++ b/ironcalc_base/expressions/parser/stringify/index.html @@ -0,0 +1 @@ +ironcalc_base::expressions::parser::stringify - Rust
\ No newline at end of file diff --git a/ironcalc_base/expressions/parser/stringify/sidebar-items.js b/ironcalc_base/expressions/parser/stringify/sidebar-items.js new file mode 100644 index 0000000..9cf1aa5 --- /dev/null +++ b/ironcalc_base/expressions/parser/stringify/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["DisplaceData"],"fn":["to_excel_string","to_rc_format","to_string","to_string_displaced"]}; \ No newline at end of file diff --git a/ironcalc_base/expressions/parser/struct.Parser.html b/ironcalc_base/expressions/parser/struct.Parser.html new file mode 100644 index 0000000..d7df69b --- /dev/null +++ b/ironcalc_base/expressions/parser/struct.Parser.html @@ -0,0 +1,17 @@ +Parser in ironcalc_base::expressions::parser - Rust
pub struct Parser { /* private fields */ }

Implementations§

source§

impl Parser

source

pub fn new(worksheets: Vec<String>, tables: HashMap<String, Table>) -> Parser

source

pub fn set_lexer_mode(&mut self, mode: LexerMode)

source

pub fn set_worksheets(&mut self, worksheets: Vec<String>)

source

pub fn parse( + &mut self, + formula: &str, + context: &Option<CellReferenceRC> +) -> Node

Trait Implementations§

source§

impl Clone for Parser

source§

fn clone(&self) -> Parser

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/parser/walk/index.html b/ironcalc_base/expressions/parser/walk/index.html new file mode 100644 index 0000000..9df4ca8 --- /dev/null +++ b/ironcalc_base/expressions/parser/walk/index.html @@ -0,0 +1 @@ +ironcalc_base::expressions::parser::walk - Rust
\ No newline at end of file diff --git a/ironcalc_base/expressions/parser/walk/sidebar-items.js b/ironcalc_base/expressions/parser/walk/sidebar-items.js new file mode 100644 index 0000000..5244ce0 --- /dev/null +++ b/ironcalc_base/expressions/parser/walk/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {}; \ No newline at end of file diff --git a/ironcalc_base/expressions/sidebar-items.js b/ironcalc_base/expressions/sidebar-items.js new file mode 100644 index 0000000..5a9650c --- /dev/null +++ b/ironcalc_base/expressions/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["lexer","parser","token","types","utils"]}; \ No newline at end of file diff --git a/ironcalc_base/expressions/token/enum.Error.html b/ironcalc_base/expressions/token/enum.Error.html new file mode 100644 index 0000000..48816e1 --- /dev/null +++ b/ironcalc_base/expressions/token/enum.Error.html @@ -0,0 +1,39 @@ +Error in ironcalc_base::expressions::token - Rust
#[repr(u8)]
pub enum Error { + REF = 0, + NAME = 1, + VALUE = 2, + DIV = 3, + NA = 4, + NUM = 5, + ERROR = 6, + NIMPL = 7, + SPILL = 8, + CALC = 9, + CIRC = 10, + NULL = 11, +}
Expand description

List of errors +Note that “#ERROR!” and “#N/IMPL!” are not part of the xlsx standard

+
    +
  • “#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 +Note that they are serialized/deserialized by index
  • +
+

Variants§

§

REF = 0

§

NAME = 1

§

VALUE = 2

§

DIV = 3

§

NA = 4

§

NUM = 5

§

ERROR = 6

§

NIMPL = 7

§

SPILL = 8

§

CALC = 9

§

CIRC = 10

§

NULL = 11

Implementations§

Trait Implementations§

source§

impl Clone for Error

source§

fn clone(&self) -> Error

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Error

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Error

source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>where + D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Display for Error

source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Error

source§

fn eq(&self, other: &Error) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Error

source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>where + S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for Error

source§

impl StructuralEq for Error

source§

impl StructuralPartialEq for Error

Auto Trait Implementations§

§

impl RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl UnwindSafe for Error

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/expressions/token/enum.OpCompare.html b/ironcalc_base/expressions/token/enum.OpCompare.html new file mode 100644 index 0000000..2ae56d5 --- /dev/null +++ b/ironcalc_base/expressions/token/enum.OpCompare.html @@ -0,0 +1,23 @@ +OpCompare in ironcalc_base::expressions::token - Rust
pub enum OpCompare {
+    LessThan,
+    GreaterThan,
+    Equal,
+    LessOrEqualThan,
+    GreaterOrEqualThan,
+    NonEqual,
+}

Variants§

§

LessThan

§

GreaterThan

§

Equal

§

LessOrEqualThan

§

GreaterOrEqualThan

§

NonEqual

Trait Implementations§

source§

impl Clone for OpCompare

source§

fn clone(&self) -> OpCompare

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for OpCompare

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for OpCompare

source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for OpCompare

source§

fn eq(&self, other: &OpCompare) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for OpCompare

source§

impl StructuralEq for OpCompare

source§

impl StructuralPartialEq for OpCompare

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/token/enum.OpProduct.html b/ironcalc_base/expressions/token/enum.OpProduct.html new file mode 100644 index 0000000..d68f1c0 --- /dev/null +++ b/ironcalc_base/expressions/token/enum.OpProduct.html @@ -0,0 +1,19 @@ +OpProduct in ironcalc_base::expressions::token - Rust
pub enum OpProduct {
+    Times,
+    Divide,
+}

Variants§

§

Times

§

Divide

Trait Implementations§

source§

impl Clone for OpProduct

source§

fn clone(&self) -> OpProduct

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for OpProduct

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for OpProduct

source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for OpProduct

source§

fn eq(&self, other: &OpProduct) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for OpProduct

source§

impl StructuralEq for OpProduct

source§

impl StructuralPartialEq for OpProduct

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/token/enum.OpSum.html b/ironcalc_base/expressions/token/enum.OpSum.html new file mode 100644 index 0000000..2984a9f --- /dev/null +++ b/ironcalc_base/expressions/token/enum.OpSum.html @@ -0,0 +1,19 @@ +OpSum in ironcalc_base::expressions::token - Rust
pub enum OpSum {
+    Add,
+    Minus,
+}

Variants§

§

Add

§

Minus

Trait Implementations§

source§

impl Clone for OpSum

source§

fn clone(&self) -> OpSum

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for OpSum

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for OpSum

source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for OpSum

source§

fn eq(&self, other: &OpSum) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for OpSum

source§

impl StructuralEq for OpSum

source§

impl StructuralPartialEq for OpSum

Auto Trait Implementations§

§

impl RefUnwindSafe for OpSum

§

impl Send for OpSum

§

impl Sync for OpSum

§

impl Unpin for OpSum

§

impl UnwindSafe for OpSum

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/token/enum.OpUnary.html b/ironcalc_base/expressions/token/enum.OpUnary.html new file mode 100644 index 0000000..f90ecdb --- /dev/null +++ b/ironcalc_base/expressions/token/enum.OpUnary.html @@ -0,0 +1,19 @@ +OpUnary in ironcalc_base::expressions::token - Rust
pub enum OpUnary {
+    Minus,
+    Percentage,
+}

Variants§

§

Minus

§

Percentage

Trait Implementations§

source§

impl Clone for OpUnary

source§

fn clone(&self) -> OpUnary

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for OpUnary

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for OpUnary

source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for OpUnary

source§

fn eq(&self, other: &OpUnary) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for OpUnary

source§

impl StructuralEq for OpUnary

source§

impl StructuralPartialEq for OpUnary

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/token/enum.TableReference.html b/ironcalc_base/expressions/token/enum.TableReference.html new file mode 100644 index 0000000..0a53f49 --- /dev/null +++ b/ironcalc_base/expressions/token/enum.TableReference.html @@ -0,0 +1,18 @@ +TableReference in ironcalc_base::expressions::token - Rust
pub enum TableReference {
+    ColumnReference(String),
+    RangeReference((String, String)),
+}

Variants§

§

ColumnReference(String)

§

RangeReference((String, String))

Trait Implementations§

source§

impl Clone for TableReference

source§

fn clone(&self) -> TableReference

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for TableReference

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for TableReference

source§

fn eq(&self, other: &TableReference) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl StructuralPartialEq for TableReference

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/token/enum.TableSpecifier.html b/ironcalc_base/expressions/token/enum.TableSpecifier.html new file mode 100644 index 0000000..a792456 --- /dev/null +++ b/ironcalc_base/expressions/token/enum.TableSpecifier.html @@ -0,0 +1,21 @@ +TableSpecifier in ironcalc_base::expressions::token - Rust
pub enum TableSpecifier {
+    All,
+    Data,
+    Headers,
+    ThisRow,
+    Totals,
+}

Variants§

§

All

§

Data

§

Headers

§

ThisRow

§

Totals

Trait Implementations§

source§

impl Clone for TableSpecifier

source§

fn clone(&self) -> TableSpecifier

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for TableSpecifier

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for TableSpecifier

source§

fn eq(&self, other: &TableSpecifier) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl StructuralPartialEq for TableSpecifier

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/token/enum.TokenType.html b/ironcalc_base/expressions/token/enum.TokenType.html new file mode 100644 index 0000000..e9f2ffd --- /dev/null +++ b/ironcalc_base/expressions/token/enum.TokenType.html @@ -0,0 +1,57 @@ +TokenType in ironcalc_base::expressions::token - Rust
pub enum TokenType {
+
Show 26 variants Illegal(LexerError), + EOF, + Ident(String), + String(String), + Number(f64), + Boolean(bool), + Error(Error), + Compare(OpCompare), + Addition(OpSum), + Product(OpProduct), + Power, + LeftParenthesis, + RightParenthesis, + Colon, + Semicolon, + LeftBracket, + RightBracket, + LeftBrace, + RightBrace, + Comma, + Bang, + Percent, + And, + Reference { + sheet: Option<String>, + row: i32, + column: i32, + absolute_column: bool, + absolute_row: bool, + }, + Range { + sheet: Option<String>, + left: ParsedReference, + right: ParsedReference, + }, + StructuredReference { + table_name: String, + specifier: Option<TableSpecifier>, + table_reference: Option<TableReference>, + }, +
}

Variants§

§

Illegal(LexerError)

§

EOF

§

Ident(String)

§

String(String)

§

Number(f64)

§

Boolean(bool)

§

Error(Error)

§

Compare(OpCompare)

§

Addition(OpSum)

§

Product(OpProduct)

§

Power

§

LeftParenthesis

§

RightParenthesis

§

Colon

§

Semicolon

§

LeftBracket

§

RightBracket

§

LeftBrace

§

RightBrace

§

Comma

§

Bang

§

Percent

§

And

§

Reference

Fields

§row: i32
§column: i32
§absolute_column: bool
§absolute_row: bool
§

Range

§

StructuredReference

Fields

§table_name: String
§table_reference: Option<TableReference>

Trait Implementations§

source§

impl Clone for TokenType

source§

fn clone(&self) -> TokenType

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for TokenType

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for TokenType

source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for TokenType

source§

fn eq(&self, other: &TokenType) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl StructuralPartialEq for TokenType

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/token/fn.get_error_by_english_name.html b/ironcalc_base/expressions/token/fn.get_error_by_english_name.html new file mode 100644 index 0000000..9892bc1 --- /dev/null +++ b/ironcalc_base/expressions/token/fn.get_error_by_english_name.html @@ -0,0 +1 @@ +get_error_by_english_name in ironcalc_base::expressions::token - Rust
pub fn get_error_by_english_name(name: &str) -> Option<Error>
\ No newline at end of file diff --git a/ironcalc_base/expressions/token/fn.get_error_by_name.html b/ironcalc_base/expressions/token/fn.get_error_by_name.html new file mode 100644 index 0000000..9ddb1cb --- /dev/null +++ b/ironcalc_base/expressions/token/fn.get_error_by_name.html @@ -0,0 +1 @@ +get_error_by_name in ironcalc_base::expressions::token - Rust
pub fn get_error_by_name(name: &str, language: &Language) -> Option<Error>
\ No newline at end of file diff --git a/ironcalc_base/expressions/token/fn.index.html b/ironcalc_base/expressions/token/fn.index.html new file mode 100644 index 0000000..6ed5400 --- /dev/null +++ b/ironcalc_base/expressions/token/fn.index.html @@ -0,0 +1 @@ +index in ironcalc_base::expressions::token - Rust
pub fn index(token: &TokenType) -> u32
\ No newline at end of file diff --git a/ironcalc_base/expressions/token/fn.is_english_error_string.html b/ironcalc_base/expressions/token/fn.is_english_error_string.html new file mode 100644 index 0000000..e0eadad --- /dev/null +++ b/ironcalc_base/expressions/token/fn.is_english_error_string.html @@ -0,0 +1 @@ +is_english_error_string in ironcalc_base::expressions::token - Rust
pub fn is_english_error_string(name: &str) -> bool
\ No newline at end of file diff --git a/ironcalc_base/expressions/token/index.html b/ironcalc_base/expressions/token/index.html new file mode 100644 index 0000000..54f5684 --- /dev/null +++ b/ironcalc_base/expressions/token/index.html @@ -0,0 +1,2 @@ +ironcalc_base::expressions::token - Rust
\ No newline at end of file diff --git a/ironcalc_base/expressions/token/sidebar-items.js b/ironcalc_base/expressions/token/sidebar-items.js new file mode 100644 index 0000000..6a14aae --- /dev/null +++ b/ironcalc_base/expressions/token/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Error","OpCompare","OpProduct","OpSum","OpUnary","TableReference","TableSpecifier","TokenType"],"fn":["get_error_by_english_name","get_error_by_name","index","is_english_error_string"]}; \ No newline at end of file diff --git a/ironcalc_base/expressions/types/index.html b/ironcalc_base/expressions/types/index.html new file mode 100644 index 0000000..6b2dab6 --- /dev/null +++ b/ironcalc_base/expressions/types/index.html @@ -0,0 +1,3 @@ +ironcalc_base::expressions::types - Rust

Structs

\ No newline at end of file diff --git a/ironcalc_base/expressions/types/sidebar-items.js b/ironcalc_base/expressions/types/sidebar-items.js new file mode 100644 index 0000000..40bfcfc --- /dev/null +++ b/ironcalc_base/expressions/types/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["Area","CellReference","CellReferenceIndex","CellReferenceRC","ParsedRange","ParsedReference"]}; \ No newline at end of file diff --git a/ironcalc_base/expressions/types/struct.Area.html b/ironcalc_base/expressions/types/struct.Area.html new file mode 100644 index 0000000..faff9e7 --- /dev/null +++ b/ironcalc_base/expressions/types/struct.Area.html @@ -0,0 +1,21 @@ +Area in ironcalc_base::expressions::types - Rust
pub struct Area {
+    pub sheet: u32,
+    pub row: i32,
+    pub column: i32,
+    pub width: i32,
+    pub height: i32,
+}

Fields§

§sheet: u32§row: i32§column: i32§width: i32§height: i32

Trait Implementations§

source§

impl<'de> Deserialize<'de> for Area

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for Area

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl RefUnwindSafe for Area

§

impl Send for Area

§

impl Sync for Area

§

impl Unpin for Area

§

impl UnwindSafe for Area

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/expressions/types/struct.CellReference.html b/ironcalc_base/expressions/types/struct.CellReference.html new file mode 100644 index 0000000..b6574cf --- /dev/null +++ b/ironcalc_base/expressions/types/struct.CellReference.html @@ -0,0 +1,16 @@ +CellReference in ironcalc_base::expressions::types - Rust
pub struct CellReference {
+    pub sheet: String,
+    pub column: String,
+    pub row: String,
+}

Fields§

§sheet: String§column: String§row: String

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/types/struct.CellReferenceIndex.html b/ironcalc_base/expressions/types/struct.CellReferenceIndex.html new file mode 100644 index 0000000..a61577b --- /dev/null +++ b/ironcalc_base/expressions/types/struct.CellReferenceIndex.html @@ -0,0 +1,22 @@ +CellReferenceIndex in ironcalc_base::expressions::types - Rust
pub struct CellReferenceIndex {
+    pub sheet: u32,
+    pub column: i32,
+    pub row: i32,
+}

Fields§

§sheet: u32§column: i32§row: i32

Trait Implementations§

source§

impl Clone for CellReferenceIndex

source§

fn clone(&self) -> CellReferenceIndex

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CellReferenceIndex

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for CellReferenceIndex

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for CellReferenceIndex

source§

fn eq(&self, other: &CellReferenceIndex) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for CellReferenceIndex

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for CellReferenceIndex

source§

impl StructuralEq for CellReferenceIndex

source§

impl StructuralPartialEq for CellReferenceIndex

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/expressions/types/struct.CellReferenceRC.html b/ironcalc_base/expressions/types/struct.CellReferenceRC.html new file mode 100644 index 0000000..8ccadba --- /dev/null +++ b/ironcalc_base/expressions/types/struct.CellReferenceRC.html @@ -0,0 +1,17 @@ +CellReferenceRC in ironcalc_base::expressions::types - Rust
pub struct CellReferenceRC {
+    pub sheet: String,
+    pub column: i32,
+    pub row: i32,
+}

Fields§

§sheet: String§column: i32§row: i32

Trait Implementations§

source§

impl Clone for CellReferenceRC

source§

fn clone(&self) -> CellReferenceRC

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/types/struct.ParsedRange.html b/ironcalc_base/expressions/types/struct.ParsedRange.html new file mode 100644 index 0000000..1a86eaf --- /dev/null +++ b/ironcalc_base/expressions/types/struct.ParsedRange.html @@ -0,0 +1,18 @@ +ParsedRange in ironcalc_base::expressions::types - Rust
pub struct ParsedRange {
+    pub left: ParsedReference,
+    pub right: Option<ParsedReference>,
+}
Expand description

If right is None it is just a reference +Column ranges like D:D will have absolute_row=true and left.row=1 and right.row=LAST_ROW +Row ranges like 5:5 will have absolute_column=true and left.column=1 and right.column=LAST_COLUMN

+

Fields§

§left: ParsedReference§right: Option<ParsedReference>

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/expressions/types/struct.ParsedReference.html b/ironcalc_base/expressions/types/struct.ParsedReference.html new file mode 100644 index 0000000..a4be678 --- /dev/null +++ b/ironcalc_base/expressions/types/struct.ParsedReference.html @@ -0,0 +1,23 @@ +ParsedReference in ironcalc_base::expressions::types - Rust
pub struct ParsedReference {
+    pub column: i32,
+    pub row: i32,
+    pub absolute_column: bool,
+    pub absolute_row: bool,
+}

Fields§

§column: i32§row: i32§absolute_column: bool§absolute_row: bool

Trait Implementations§

source§

impl Clone for ParsedReference

source§

fn clone(&self) -> ParsedReference

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ParsedReference

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for ParsedReference

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for ParsedReference

source§

fn eq(&self, other: &ParsedReference) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for ParsedReference

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for ParsedReference

source§

impl StructuralEq for ParsedReference

source§

impl StructuralPartialEq for ParsedReference

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/expressions/utils/fn.column_to_number.html b/ironcalc_base/expressions/utils/fn.column_to_number.html new file mode 100644 index 0000000..5394b93 --- /dev/null +++ b/ironcalc_base/expressions/utils/fn.column_to_number.html @@ -0,0 +1,2 @@ +column_to_number in ironcalc_base::expressions::utils - Rust
pub fn column_to_number(column: &str) -> Result<i32, String>
Expand description

Converts column letter identifier to number.

+
\ No newline at end of file diff --git a/ironcalc_base/expressions/utils/fn.is_valid_column.html b/ironcalc_base/expressions/utils/fn.is_valid_column.html new file mode 100644 index 0000000..7811548 --- /dev/null +++ b/ironcalc_base/expressions/utils/fn.is_valid_column.html @@ -0,0 +1 @@ +is_valid_column in ironcalc_base::expressions::utils - Rust
pub fn is_valid_column(column: &str) -> bool
\ No newline at end of file diff --git a/ironcalc_base/expressions/utils/fn.is_valid_column_number.html b/ironcalc_base/expressions/utils/fn.is_valid_column_number.html new file mode 100644 index 0000000..bc788e3 --- /dev/null +++ b/ironcalc_base/expressions/utils/fn.is_valid_column_number.html @@ -0,0 +1,2 @@ +is_valid_column_number in ironcalc_base::expressions::utils - Rust
pub fn is_valid_column_number(column: i32) -> bool
Expand description

Checks if column number is in valid range.

+
\ No newline at end of file diff --git a/ironcalc_base/expressions/utils/fn.is_valid_identifier.html b/ironcalc_base/expressions/utils/fn.is_valid_identifier.html new file mode 100644 index 0000000..2c95a69 --- /dev/null +++ b/ironcalc_base/expressions/utils/fn.is_valid_identifier.html @@ -0,0 +1 @@ +is_valid_identifier in ironcalc_base::expressions::utils - Rust
pub fn is_valid_identifier(name: &str) -> bool
\ No newline at end of file diff --git a/ironcalc_base/expressions/utils/fn.is_valid_row.html b/ironcalc_base/expressions/utils/fn.is_valid_row.html new file mode 100644 index 0000000..430c144 --- /dev/null +++ b/ironcalc_base/expressions/utils/fn.is_valid_row.html @@ -0,0 +1 @@ +is_valid_row in ironcalc_base::expressions::utils - Rust
pub fn is_valid_row(row: i32) -> bool
\ No newline at end of file diff --git a/ironcalc_base/expressions/utils/fn.number_to_column.html b/ironcalc_base/expressions/utils/fn.number_to_column.html new file mode 100644 index 0000000..7eac2ed --- /dev/null +++ b/ironcalc_base/expressions/utils/fn.number_to_column.html @@ -0,0 +1,2 @@ +number_to_column in ironcalc_base::expressions::utils - Rust
pub fn number_to_column(i: i32) -> Option<String>
Expand description

If input number is outside valid range None is returned.

+
\ No newline at end of file diff --git a/ironcalc_base/expressions/utils/fn.parse_reference_a1.html b/ironcalc_base/expressions/utils/fn.parse_reference_a1.html new file mode 100644 index 0000000..119582f --- /dev/null +++ b/ironcalc_base/expressions/utils/fn.parse_reference_a1.html @@ -0,0 +1 @@ +parse_reference_a1 in ironcalc_base::expressions::utils - Rust
pub fn parse_reference_a1(r: &str) -> Option<ParsedReference>
\ No newline at end of file diff --git a/ironcalc_base/expressions/utils/fn.parse_reference_r1c1.html b/ironcalc_base/expressions/utils/fn.parse_reference_r1c1.html new file mode 100644 index 0000000..b91d36e --- /dev/null +++ b/ironcalc_base/expressions/utils/fn.parse_reference_r1c1.html @@ -0,0 +1 @@ +parse_reference_r1c1 in ironcalc_base::expressions::utils - Rust
pub fn parse_reference_r1c1(r: &str) -> Option<ParsedReference>
\ No newline at end of file diff --git a/ironcalc_base/expressions/utils/fn.quote_name.html b/ironcalc_base/expressions/utils/fn.quote_name.html new file mode 100644 index 0000000..e8894c7 --- /dev/null +++ b/ironcalc_base/expressions/utils/fn.quote_name.html @@ -0,0 +1,3 @@ +quote_name in ironcalc_base::expressions::utils - Rust
pub fn quote_name(name: &str) -> String
Expand description

Quotes a string sheet name if it needs to +NOTE: Invalid characters in a sheet name , /, *, [, ], :, ?

+
\ No newline at end of file diff --git a/ironcalc_base/expressions/utils/index.html b/ironcalc_base/expressions/utils/index.html new file mode 100644 index 0000000..b6ef8a8 --- /dev/null +++ b/ironcalc_base/expressions/utils/index.html @@ -0,0 +1,2 @@ +ironcalc_base::expressions::utils - Rust

Functions

\ No newline at end of file diff --git a/ironcalc_base/expressions/utils/sidebar-items.js b/ironcalc_base/expressions/utils/sidebar-items.js new file mode 100644 index 0000000..d1ac73c --- /dev/null +++ b/ironcalc_base/expressions/utils/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["column_to_number","is_valid_column","is_valid_column_number","is_valid_identifier","is_valid_row","number_to_column","parse_reference_a1","parse_reference_r1c1","quote_name"]}; \ No newline at end of file diff --git a/ironcalc_base/formatter/dates/fn.date_to_serial_number.html b/ironcalc_base/formatter/dates/fn.date_to_serial_number.html new file mode 100644 index 0000000..08665d5 --- /dev/null +++ b/ironcalc_base/formatter/dates/fn.date_to_serial_number.html @@ -0,0 +1,5 @@ +date_to_serial_number in ironcalc_base::formatter::dates - Rust
pub fn date_to_serial_number(
+    day: u32,
+    month: u32,
+    year: i32
+) -> Result<i32, String>
\ No newline at end of file diff --git a/ironcalc_base/formatter/dates/fn.from_excel_date.html b/ironcalc_base/formatter/dates/fn.from_excel_date.html new file mode 100644 index 0000000..7059e55 --- /dev/null +++ b/ironcalc_base/formatter/dates/fn.from_excel_date.html @@ -0,0 +1 @@ +from_excel_date in ironcalc_base::formatter::dates - Rust
pub fn from_excel_date(days: i64) -> NaiveDate
\ No newline at end of file diff --git a/ironcalc_base/formatter/dates/index.html b/ironcalc_base/formatter/dates/index.html new file mode 100644 index 0000000..08155dc --- /dev/null +++ b/ironcalc_base/formatter/dates/index.html @@ -0,0 +1 @@ +ironcalc_base::formatter::dates - Rust
\ No newline at end of file diff --git a/ironcalc_base/formatter/dates/sidebar-items.js b/ironcalc_base/formatter/dates/sidebar-items.js new file mode 100644 index 0000000..2c8174e --- /dev/null +++ b/ironcalc_base/formatter/dates/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["date_to_serial_number","from_excel_date"]}; \ No newline at end of file diff --git a/ironcalc_base/formatter/format/fn.format_number.html b/ironcalc_base/formatter/format/fn.format_number.html new file mode 100644 index 0000000..400ba61 --- /dev/null +++ b/ironcalc_base/formatter/format/fn.format_number.html @@ -0,0 +1,5 @@ +format_number in ironcalc_base::formatter::format - Rust
pub fn format_number(
+    value_original: f64,
+    format: &str,
+    locale: &Locale
+) -> Formatted
\ No newline at end of file diff --git a/ironcalc_base/formatter/format/index.html b/ironcalc_base/formatter/format/index.html new file mode 100644 index 0000000..157b059 --- /dev/null +++ b/ironcalc_base/formatter/format/index.html @@ -0,0 +1 @@ +ironcalc_base::formatter::format - Rust
\ No newline at end of file diff --git a/ironcalc_base/formatter/format/sidebar-items.js b/ironcalc_base/formatter/format/sidebar-items.js new file mode 100644 index 0000000..a8d0fff --- /dev/null +++ b/ironcalc_base/formatter/format/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["format_number"],"struct":["Formatted"]}; \ No newline at end of file diff --git a/ironcalc_base/formatter/format/struct.Formatted.html b/ironcalc_base/formatter/format/struct.Formatted.html new file mode 100644 index 0000000..717f1e0 --- /dev/null +++ b/ironcalc_base/formatter/format/struct.Formatted.html @@ -0,0 +1,16 @@ +Formatted in ironcalc_base::formatter::format - Rust
pub struct Formatted {
+    pub color: Option<i32>,
+    pub text: String,
+    pub error: Option<String>,
+}

Fields§

§color: Option<i32>§text: String§error: Option<String>

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/formatter/index.html b/ironcalc_base/formatter/index.html new file mode 100644 index 0000000..ae04cb0 --- /dev/null +++ b/ironcalc_base/formatter/index.html @@ -0,0 +1 @@ +ironcalc_base::formatter - Rust
\ No newline at end of file diff --git a/ironcalc_base/formatter/lexer/enum.Compare.html b/ironcalc_base/formatter/lexer/enum.Compare.html new file mode 100644 index 0000000..e28ae35 --- /dev/null +++ b/ironcalc_base/formatter/lexer/enum.Compare.html @@ -0,0 +1,20 @@ +Compare in ironcalc_base::formatter::lexer - Rust
pub enum Compare {
+    Equal,
+    LessThan,
+    GreaterThan,
+    LessOrEqualThan,
+    GreaterOrEqualThan,
+}

Variants§

§

Equal

§

LessThan

§

GreaterThan

§

LessOrEqualThan

§

GreaterOrEqualThan

Trait Implementations§

source§

impl Debug for Compare

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Compare

source§

fn eq(&self, other: &Compare) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for Compare

source§

impl StructuralEq for Compare

source§

impl StructuralPartialEq for Compare

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/formatter/lexer/enum.Token.html b/ironcalc_base/formatter/lexer/enum.Token.html new file mode 100644 index 0000000..b21afa5 --- /dev/null +++ b/ironcalc_base/formatter/lexer/enum.Token.html @@ -0,0 +1,45 @@ +Token in ironcalc_base::formatter::lexer - Rust
pub enum Token {
+
Show 30 variants Color(i32), + Condition(Compare, f64), + Literal(char), + Spacer(char), + Ghost(char), + Text(String), + Separator, + Raw, + Percent, + Comma, + Period, + Sharp, + Zero, + QuestionMark, + Scientific, + ScientificMinus, + General, + Day, + DayPadded, + DayNameShort, + DayName, + Month, + MonthPadded, + MonthNameShort, + MonthName, + MonthLetter, + YearShort, + Year, + ILLEGAL, + EOF, +
}

Variants§

§

Color(i32)

§

Condition(Compare, f64)

§

Literal(char)

§

Spacer(char)

§

Ghost(char)

§

Text(String)

§

Separator

§

Raw

§

Percent

§

Comma

§

Period

§

Sharp

§

Zero

§

QuestionMark

§

Scientific

§

ScientificMinus

§

General

§

Day

§

DayPadded

§

DayNameShort

§

DayName

§

Month

§

MonthPadded

§

MonthNameShort

§

MonthName

§

MonthLetter

§

YearShort

§

Year

§

ILLEGAL

§

EOF

Implementations§

source§

impl Token

source

pub fn is_digit(&self) -> bool

source

pub fn is_date(&self) -> bool

Trait Implementations§

source§

impl Debug for Token

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for Token

source§

fn eq(&self, other: &Token) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl StructuralPartialEq for Token

Auto Trait Implementations§

§

impl RefUnwindSafe for Token

§

impl Send for Token

§

impl Sync for Token

§

impl Unpin for Token

§

impl UnwindSafe for Token

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/formatter/lexer/fn.is_likely_date_number_format.html b/ironcalc_base/formatter/lexer/fn.is_likely_date_number_format.html new file mode 100644 index 0000000..90fec9d --- /dev/null +++ b/ironcalc_base/formatter/lexer/fn.is_likely_date_number_format.html @@ -0,0 +1 @@ +is_likely_date_number_format in ironcalc_base::formatter::lexer - Rust
pub fn is_likely_date_number_format(format: &str) -> bool
\ No newline at end of file diff --git a/ironcalc_base/formatter/lexer/index.html b/ironcalc_base/formatter/lexer/index.html new file mode 100644 index 0000000..b01a59c --- /dev/null +++ b/ironcalc_base/formatter/lexer/index.html @@ -0,0 +1 @@ +ironcalc_base::formatter::lexer - Rust
\ No newline at end of file diff --git a/ironcalc_base/formatter/lexer/sidebar-items.js b/ironcalc_base/formatter/lexer/sidebar-items.js new file mode 100644 index 0000000..0aa0f6b --- /dev/null +++ b/ironcalc_base/formatter/lexer/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Compare","Token"],"fn":["is_likely_date_number_format"],"struct":["Lexer"]}; \ No newline at end of file diff --git a/ironcalc_base/formatter/lexer/struct.Lexer.html b/ironcalc_base/formatter/lexer/struct.Lexer.html new file mode 100644 index 0000000..1e7adec --- /dev/null +++ b/ironcalc_base/formatter/lexer/struct.Lexer.html @@ -0,0 +1,12 @@ +Lexer in ironcalc_base::formatter::lexer - Rust
pub struct Lexer { /* private fields */ }

Implementations§

source§

impl Lexer

source

pub fn new(format: &str) -> Lexer

source

pub fn peek_token(&mut self) -> Token

source

pub fn next_token(&mut self) -> Token

Auto Trait Implementations§

§

impl RefUnwindSafe for Lexer

§

impl Send for Lexer

§

impl Sync for Lexer

§

impl Unpin for Lexer

§

impl UnwindSafe for Lexer

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/formatter/parser/enum.ParsePart.html b/ironcalc_base/formatter/parser/enum.ParsePart.html new file mode 100644 index 0000000..16f5b78 --- /dev/null +++ b/ironcalc_base/formatter/parser/enum.ParsePart.html @@ -0,0 +1,17 @@ +ParsePart in ironcalc_base::formatter::parser - Rust
pub enum ParsePart {
+    Number(NumberPart),
+    Date(DatePart),
+    Error(ErrorPart),
+    General(GeneralPart),
+}

Variants§

Implementations§

source§

impl ParsePart

source

pub fn is_error(&self) -> bool

source

pub fn is_date(&self) -> bool

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/formatter/parser/enum.TextToken.html b/ironcalc_base/formatter/parser/enum.TextToken.html new file mode 100644 index 0000000..7dc4d5d --- /dev/null +++ b/ironcalc_base/formatter/parser/enum.TextToken.html @@ -0,0 +1,31 @@ +TextToken in ironcalc_base::formatter::parser - Rust
pub enum TextToken {
+
Show 18 variants Literal(char), + Text(String), + Ghost(char), + Spacer(char), + Raw, + Digit(Digit), + Period, + Day, + DayPadded, + DayNameShort, + DayName, + Month, + MonthPadded, + MonthNameShort, + MonthName, + MonthLetter, + YearShort, + Year, +
}

Variants§

§

Literal(char)

§

Text(String)

§

Ghost(char)

§

Spacer(char)

§

Raw

§

Digit(Digit)

§

Period

§

Day

§

DayPadded

§

DayNameShort

§

DayName

§

Month

§

MonthPadded

§

MonthNameShort

§

MonthName

§

MonthLetter

§

YearShort

§

Year

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/formatter/parser/index.html b/ironcalc_base/formatter/parser/index.html new file mode 100644 index 0000000..758f620 --- /dev/null +++ b/ironcalc_base/formatter/parser/index.html @@ -0,0 +1 @@ +ironcalc_base::formatter::parser - Rust
\ No newline at end of file diff --git a/ironcalc_base/formatter/parser/sidebar-items.js b/ironcalc_base/formatter/parser/sidebar-items.js new file mode 100644 index 0000000..3cffc6f --- /dev/null +++ b/ironcalc_base/formatter/parser/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["ParsePart","TextToken"],"struct":["DatePart","Digit","ErrorPart","GeneralPart","NumberPart","Parser"]}; \ No newline at end of file diff --git a/ironcalc_base/formatter/parser/struct.DatePart.html b/ironcalc_base/formatter/parser/struct.DatePart.html new file mode 100644 index 0000000..0405f67 --- /dev/null +++ b/ironcalc_base/formatter/parser/struct.DatePart.html @@ -0,0 +1,15 @@ +DatePart in ironcalc_base::formatter::parser - Rust
pub struct DatePart {
+    pub color: Option<i32>,
+    pub tokens: Vec<TextToken>,
+}

Fields§

§color: Option<i32>§tokens: Vec<TextToken>

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/formatter/parser/struct.Digit.html b/ironcalc_base/formatter/parser/struct.Digit.html new file mode 100644 index 0000000..bbb4cbc --- /dev/null +++ b/ironcalc_base/formatter/parser/struct.Digit.html @@ -0,0 +1,16 @@ +Digit in ironcalc_base::formatter::parser - Rust
pub struct Digit {
+    pub kind: char,
+    pub index: i32,
+    pub number: char,
+}

Fields§

§kind: char§index: i32§number: char

Auto Trait Implementations§

§

impl RefUnwindSafe for Digit

§

impl Send for Digit

§

impl Sync for Digit

§

impl Unpin for Digit

§

impl UnwindSafe for Digit

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/formatter/parser/struct.ErrorPart.html b/ironcalc_base/formatter/parser/struct.ErrorPart.html new file mode 100644 index 0000000..fc42662 --- /dev/null +++ b/ironcalc_base/formatter/parser/struct.ErrorPart.html @@ -0,0 +1,12 @@ +ErrorPart in ironcalc_base::formatter::parser - Rust
pub struct ErrorPart {}

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/formatter/parser/struct.GeneralPart.html b/ironcalc_base/formatter/parser/struct.GeneralPart.html new file mode 100644 index 0000000..881af01 --- /dev/null +++ b/ironcalc_base/formatter/parser/struct.GeneralPart.html @@ -0,0 +1,12 @@ +GeneralPart in ironcalc_base::formatter::parser - Rust
pub struct GeneralPart {}

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/formatter/parser/struct.NumberPart.html b/ironcalc_base/formatter/parser/struct.NumberPart.html new file mode 100644 index 0000000..f68ab94 --- /dev/null +++ b/ironcalc_base/formatter/parser/struct.NumberPart.html @@ -0,0 +1,24 @@ +NumberPart in ironcalc_base::formatter::parser - Rust
pub struct NumberPart {
+    pub color: Option<i32>,
+    pub condition: Option<(Compare, f64)>,
+    pub use_thousands: bool,
+    pub percent: i32,
+    pub comma: i32,
+    pub tokens: Vec<TextToken>,
+    pub digit_count: i32,
+    pub precision: i32,
+    pub is_scientific: bool,
+    pub scientific_minus: bool,
+    pub exponent_digit_count: i32,
+}

Fields§

§color: Option<i32>§condition: Option<(Compare, f64)>§use_thousands: bool§percent: i32§comma: i32§tokens: Vec<TextToken>§digit_count: i32§precision: i32§is_scientific: bool§scientific_minus: bool§exponent_digit_count: i32

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/formatter/parser/struct.Parser.html b/ironcalc_base/formatter/parser/struct.Parser.html new file mode 100644 index 0000000..ee78bc9 --- /dev/null +++ b/ironcalc_base/formatter/parser/struct.Parser.html @@ -0,0 +1,15 @@ +Parser in ironcalc_base::formatter::parser - Rust
pub struct Parser {
+    pub parts: Vec<ParsePart>,
+    /* private fields */
+}

Fields§

§parts: Vec<ParsePart>

Implementations§

source§

impl Parser

source

pub fn new(format: &str) -> Self

source

pub fn parse(&mut self)

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/formatter/sidebar-items.js b/ironcalc_base/formatter/sidebar-items.js new file mode 100644 index 0000000..38136c8 --- /dev/null +++ b/ironcalc_base/formatter/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["dates","format","lexer","parser"]}; \ No newline at end of file diff --git a/ironcalc_base/index.html b/ironcalc_base/index.html new file mode 100644 index 0000000..73c1e0c --- /dev/null +++ b/ironcalc_base/index.html @@ -0,0 +1,2 @@ +ironcalc_base - Rust
\ No newline at end of file diff --git a/ironcalc_base/language/fn.get_language.html b/ironcalc_base/language/fn.get_language.html new file mode 100644 index 0000000..4ad907f --- /dev/null +++ b/ironcalc_base/language/fn.get_language.html @@ -0,0 +1 @@ +get_language in ironcalc_base::language - Rust
pub fn get_language(id: &str) -> Result<&Language, String>
\ No newline at end of file diff --git a/ironcalc_base/language/index.html b/ironcalc_base/language/index.html new file mode 100644 index 0000000..e2be1c0 --- /dev/null +++ b/ironcalc_base/language/index.html @@ -0,0 +1 @@ +ironcalc_base::language - Rust
\ No newline at end of file diff --git a/ironcalc_base/language/sidebar-items.js b/ironcalc_base/language/sidebar-items.js new file mode 100644 index 0000000..5474116 --- /dev/null +++ b/ironcalc_base/language/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["get_language"],"struct":["Booleans","Errors","Language"]}; \ No newline at end of file diff --git a/ironcalc_base/language/struct.Booleans.html b/ironcalc_base/language/struct.Booleans.html new file mode 100644 index 0000000..4f541bb --- /dev/null +++ b/ironcalc_base/language/struct.Booleans.html @@ -0,0 +1,19 @@ +Booleans in ironcalc_base::language - Rust
pub struct Booleans {
+    pub true_value: String,
+    pub false_value: String,
+}

Fields§

§true_value: String§false_value: String

Trait Implementations§

source§

impl Clone for Booleans

source§

fn clone(&self) -> Booleans

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'de> Deserialize<'de> for Booleans

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for Booleans

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/language/struct.Errors.html b/ironcalc_base/language/struct.Errors.html new file mode 100644 index 0000000..64b1444 --- /dev/null +++ b/ironcalc_base/language/struct.Errors.html @@ -0,0 +1,29 @@ +Errors in ironcalc_base::language - Rust
pub struct Errors {
+    pub ref_value: String,
+    pub name: String,
+    pub value: String,
+    pub div: String,
+    pub na: String,
+    pub num: String,
+    pub nimpl: String,
+    pub spill: String,
+    pub calc: String,
+    pub circ: String,
+    pub error: String,
+    pub null: String,
+}

Fields§

§ref_value: String§name: String§value: String§div: String§na: String§num: String§nimpl: String§spill: String§calc: String§circ: String§error: String§null: String

Trait Implementations§

source§

impl Clone for Errors

source§

fn clone(&self) -> Errors

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'de> Deserialize<'de> for Errors

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for Errors

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/language/struct.Language.html b/ironcalc_base/language/struct.Language.html new file mode 100644 index 0000000..53fe90f --- /dev/null +++ b/ironcalc_base/language/struct.Language.html @@ -0,0 +1,19 @@ +Language in ironcalc_base::language - Rust
pub struct Language {
+    pub booleans: Booleans,
+    pub errors: Errors,
+}

Fields§

§booleans: Booleans§errors: Errors

Trait Implementations§

source§

impl Clone for Language

source§

fn clone(&self) -> Language

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'de> Deserialize<'de> for Language

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for Language

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/locale/fn.get_locale.html b/ironcalc_base/locale/fn.get_locale.html new file mode 100644 index 0000000..695ae3d --- /dev/null +++ b/ironcalc_base/locale/fn.get_locale.html @@ -0,0 +1 @@ +get_locale in ironcalc_base::locale - Rust
pub fn get_locale(_id: &str) -> Result<&Locale, String>
\ No newline at end of file diff --git a/ironcalc_base/locale/fn.get_locale_fix.html b/ironcalc_base/locale/fn.get_locale_fix.html new file mode 100644 index 0000000..d1360da --- /dev/null +++ b/ironcalc_base/locale/fn.get_locale_fix.html @@ -0,0 +1 @@ +get_locale_fix in ironcalc_base::locale - Rust
pub fn get_locale_fix(id: &str) -> Result<&Locale, String>
\ No newline at end of file diff --git a/ironcalc_base/locale/index.html b/ironcalc_base/locale/index.html new file mode 100644 index 0000000..cd24991 --- /dev/null +++ b/ironcalc_base/locale/index.html @@ -0,0 +1 @@ +ironcalc_base::locale - Rust
\ No newline at end of file diff --git a/ironcalc_base/locale/sidebar-items.js b/ironcalc_base/locale/sidebar-items.js new file mode 100644 index 0000000..f7f82bf --- /dev/null +++ b/ironcalc_base/locale/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["get_locale","get_locale_fix"],"struct":["Currency","CurrencyFormats","Dates","DecimalFormats","Locale","NumbersProperties","NumbersSymbols"]}; \ No newline at end of file diff --git a/ironcalc_base/locale/struct.Currency.html b/ironcalc_base/locale/struct.Currency.html new file mode 100644 index 0000000..6a37e31 --- /dev/null +++ b/ironcalc_base/locale/struct.Currency.html @@ -0,0 +1,19 @@ +Currency in ironcalc_base::locale - Rust
pub struct Currency {
+    pub iso: String,
+    pub symbol: String,
+}

Fields§

§iso: String§symbol: String

Trait Implementations§

source§

impl Clone for Currency

source§

fn clone(&self) -> Currency

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'de> Deserialize<'de> for Currency

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for Currency

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/locale/struct.CurrencyFormats.html b/ironcalc_base/locale/struct.CurrencyFormats.html new file mode 100644 index 0000000..50e2b12 --- /dev/null +++ b/ironcalc_base/locale/struct.CurrencyFormats.html @@ -0,0 +1,23 @@ +CurrencyFormats in ironcalc_base::locale - Rust
pub struct CurrencyFormats {
+    pub standard: String,
+    pub standard_alpha_next_to_number: Option<String>,
+    pub standard_no_currency: String,
+    pub accounting: String,
+    pub accounting_alpha_next_to_number: Option<String>,
+    pub accounting_no_currency: String,
+}

Fields§

§standard: String§standard_alpha_next_to_number: Option<String>§standard_no_currency: String§accounting: String§accounting_alpha_next_to_number: Option<String>§accounting_no_currency: String

Trait Implementations§

source§

impl Clone for CurrencyFormats

source§

fn clone(&self) -> CurrencyFormats

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'de> Deserialize<'de> for CurrencyFormats

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for CurrencyFormats

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/locale/struct.Dates.html b/ironcalc_base/locale/struct.Dates.html new file mode 100644 index 0000000..48cdd40 --- /dev/null +++ b/ironcalc_base/locale/struct.Dates.html @@ -0,0 +1,22 @@ +Dates in ironcalc_base::locale - Rust

Struct ironcalc_base::locale::Dates

source ·
pub struct Dates {
+    pub day_names: Vec<String>,
+    pub day_names_short: Vec<String>,
+    pub months: Vec<String>,
+    pub months_short: Vec<String>,
+    pub months_letter: Vec<String>,
+}

Fields§

§day_names: Vec<String>§day_names_short: Vec<String>§months: Vec<String>§months_short: Vec<String>§months_letter: Vec<String>

Trait Implementations§

source§

impl Clone for Dates

source§

fn clone(&self) -> Dates

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'de> Deserialize<'de> for Dates

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for Dates

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl RefUnwindSafe for Dates

§

impl Send for Dates

§

impl Sync for Dates

§

impl Unpin for Dates

§

impl UnwindSafe for Dates

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/locale/struct.DecimalFormats.html b/ironcalc_base/locale/struct.DecimalFormats.html new file mode 100644 index 0000000..80ca9cc --- /dev/null +++ b/ironcalc_base/locale/struct.DecimalFormats.html @@ -0,0 +1,18 @@ +DecimalFormats in ironcalc_base::locale - Rust
pub struct DecimalFormats {
+    pub standard: String,
+}

Fields§

§standard: String

Trait Implementations§

source§

impl Clone for DecimalFormats

source§

fn clone(&self) -> DecimalFormats

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'de> Deserialize<'de> for DecimalFormats

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for DecimalFormats

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/locale/struct.Locale.html b/ironcalc_base/locale/struct.Locale.html new file mode 100644 index 0000000..cb85c43 --- /dev/null +++ b/ironcalc_base/locale/struct.Locale.html @@ -0,0 +1,20 @@ +Locale in ironcalc_base::locale - Rust
pub struct Locale {
+    pub dates: Dates,
+    pub numbers: NumbersProperties,
+    pub currency: Currency,
+}

Fields§

§dates: Dates§numbers: NumbersProperties§currency: Currency

Trait Implementations§

source§

impl Clone for Locale

source§

fn clone(&self) -> Locale

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'de> Deserialize<'de> for Locale

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for Locale

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/locale/struct.NumbersProperties.html b/ironcalc_base/locale/struct.NumbersProperties.html new file mode 100644 index 0000000..6cdb2ce --- /dev/null +++ b/ironcalc_base/locale/struct.NumbersProperties.html @@ -0,0 +1,20 @@ +NumbersProperties in ironcalc_base::locale - Rust
pub struct NumbersProperties {
+    pub symbols: NumbersSymbols,
+    pub decimal_formats: DecimalFormats,
+    pub currency_formats: CurrencyFormats,
+}

Fields§

§symbols: NumbersSymbols§decimal_formats: DecimalFormats§currency_formats: CurrencyFormats

Trait Implementations§

source§

impl Clone for NumbersProperties

source§

fn clone(&self) -> NumbersProperties

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'de> Deserialize<'de> for NumbersProperties

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for NumbersProperties

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/locale/struct.NumbersSymbols.html b/ironcalc_base/locale/struct.NumbersSymbols.html new file mode 100644 index 0000000..105798a --- /dev/null +++ b/ironcalc_base/locale/struct.NumbersSymbols.html @@ -0,0 +1,30 @@ +NumbersSymbols in ironcalc_base::locale - Rust
pub struct NumbersSymbols {
Show 13 fields + pub decimal: String, + pub group: String, + pub list: String, + pub percent_sign: String, + pub plus_sign: String, + pub minus_sign: String, + pub approximately_sign: String, + pub exponential: String, + pub superscripting_exponent: String, + pub per_mille: String, + pub infinity: String, + pub nan: String, + pub time_separator: String, +
}

Fields§

§decimal: String§group: String§list: String§percent_sign: String§plus_sign: String§minus_sign: String§approximately_sign: String§exponential: String§superscripting_exponent: String§per_mille: String§infinity: String§nan: String§time_separator: String

Trait Implementations§

source§

impl Clone for NumbersSymbols

source§

fn clone(&self) -> NumbersSymbols

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'de> Deserialize<'de> for NumbersSymbols

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for NumbersSymbols

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/model/enum.CellState.html b/ironcalc_base/model/enum.CellState.html new file mode 100644 index 0000000..db3ef31 --- /dev/null +++ b/ironcalc_base/model/enum.CellState.html @@ -0,0 +1,16 @@ +CellState in ironcalc_base::model - Rust
pub enum CellState {
+    Evaluated,
+    Evaluating,
+}

Variants§

§

Evaluated

§

Evaluating

Trait Implementations§

source§

impl Clone for CellState

source§

fn clone(&self) -> CellState

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/model/enum.ParsedDefinedName.html b/ironcalc_base/model/enum.ParsedDefinedName.html new file mode 100644 index 0000000..4a8eb2a --- /dev/null +++ b/ironcalc_base/model/enum.ParsedDefinedName.html @@ -0,0 +1,17 @@ +ParsedDefinedName in ironcalc_base::model - Rust
pub enum ParsedDefinedName {
+    CellReference(CellReference),
+    RangeReference(Range),
+    InvalidDefinedNameFormula,
+}

Variants§

§

CellReference(CellReference)

§

RangeReference(Range)

§

InvalidDefinedNameFormula

Trait Implementations§

source§

impl Clone for ParsedDefinedName

source§

fn clone(&self) -> ParsedDefinedName

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for ParsedDefinedName

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/model/enum.Tz.html b/ironcalc_base/model/enum.Tz.html new file mode 100644 index 0000000..179c76b --- /dev/null +++ b/ironcalc_base/model/enum.Tz.html @@ -0,0 +1,1266 @@ +Tz in ironcalc_base::model - Rust

Enum ironcalc_base::model::Tz

pub enum Tz {
+
Show 595 variants Africa__Abidjan, + Africa__Accra, + Africa__Addis_Ababa, + Africa__Algiers, + Africa__Asmara, + Africa__Asmera, + Africa__Bamako, + Africa__Bangui, + Africa__Banjul, + Africa__Bissau, + Africa__Blantyre, + Africa__Brazzaville, + Africa__Bujumbura, + Africa__Cairo, + Africa__Casablanca, + Africa__Ceuta, + Africa__Conakry, + Africa__Dakar, + Africa__Dar_es_Salaam, + Africa__Djibouti, + Africa__Douala, + Africa__El_Aaiun, + Africa__Freetown, + Africa__Gaborone, + Africa__Harare, + Africa__Johannesburg, + Africa__Juba, + Africa__Kampala, + Africa__Khartoum, + Africa__Kigali, + Africa__Kinshasa, + Africa__Lagos, + Africa__Libreville, + Africa__Lome, + Africa__Luanda, + Africa__Lubumbashi, + Africa__Lusaka, + Africa__Malabo, + Africa__Maputo, + Africa__Maseru, + Africa__Mbabane, + Africa__Mogadishu, + Africa__Monrovia, + Africa__Nairobi, + Africa__Ndjamena, + Africa__Niamey, + Africa__Nouakchott, + Africa__Ouagadougou, + Africa__PortoNovo, + Africa__Sao_Tome, + Africa__Timbuktu, + Africa__Tripoli, + Africa__Tunis, + Africa__Windhoek, + America__Adak, + America__Anchorage, + America__Anguilla, + America__Antigua, + America__Araguaina, + America__Argentina__Buenos_Aires, + America__Argentina__Catamarca, + America__Argentina__ComodRivadavia, + America__Argentina__Cordoba, + America__Argentina__Jujuy, + America__Argentina__La_Rioja, + America__Argentina__Mendoza, + America__Argentina__Rio_Gallegos, + America__Argentina__Salta, + America__Argentina__San_Juan, + America__Argentina__San_Luis, + America__Argentina__Tucuman, + America__Argentina__Ushuaia, + America__Aruba, + America__Asuncion, + America__Atikokan, + America__Atka, + America__Bahia, + America__Bahia_Banderas, + America__Barbados, + America__Belem, + America__Belize, + America__BlancSablon, + America__Boa_Vista, + America__Bogota, + America__Boise, + America__Buenos_Aires, + America__Cambridge_Bay, + America__Campo_Grande, + America__Cancun, + America__Caracas, + America__Catamarca, + America__Cayenne, + America__Cayman, + America__Chicago, + America__Chihuahua, + America__Coral_Harbour, + America__Cordoba, + America__Costa_Rica, + America__Creston, + America__Cuiaba, + America__Curacao, + America__Danmarkshavn, + America__Dawson, + America__Dawson_Creek, + America__Denver, + America__Detroit, + America__Dominica, + America__Edmonton, + America__Eirunepe, + America__El_Salvador, + America__Ensenada, + America__Fort_Nelson, + America__Fort_Wayne, + America__Fortaleza, + America__Glace_Bay, + America__Godthab, + America__Goose_Bay, + America__Grand_Turk, + America__Grenada, + America__Guadeloupe, + America__Guatemala, + America__Guayaquil, + America__Guyana, + America__Halifax, + America__Havana, + America__Hermosillo, + America__Indiana__Indianapolis, + America__Indiana__Knox, + America__Indiana__Marengo, + America__Indiana__Petersburg, + America__Indiana__Tell_City, + America__Indiana__Vevay, + America__Indiana__Vincennes, + America__Indiana__Winamac, + America__Indianapolis, + America__Inuvik, + America__Iqaluit, + America__Jamaica, + America__Jujuy, + America__Juneau, + America__Kentucky__Louisville, + America__Kentucky__Monticello, + America__Knox_IN, + America__Kralendijk, + America__La_Paz, + America__Lima, + America__Los_Angeles, + America__Louisville, + America__Lower_Princes, + America__Maceio, + America__Managua, + America__Manaus, + America__Marigot, + America__Martinique, + America__Matamoros, + America__Mazatlan, + America__Mendoza, + America__Menominee, + America__Merida, + America__Metlakatla, + America__Mexico_City, + America__Miquelon, + America__Moncton, + America__Monterrey, + America__Montevideo, + America__Montreal, + America__Montserrat, + America__Nassau, + America__New_York, + America__Nipigon, + America__Nome, + America__Noronha, + America__North_Dakota__Beulah, + America__North_Dakota__Center, + America__North_Dakota__New_Salem, + America__Nuuk, + America__Ojinaga, + America__Panama, + America__Pangnirtung, + America__Paramaribo, + America__Phoenix, + America__PortauPrince, + America__Port_of_Spain, + America__Porto_Acre, + America__Porto_Velho, + America__Puerto_Rico, + America__Punta_Arenas, + America__Rainy_River, + America__Rankin_Inlet, + America__Recife, + America__Regina, + America__Resolute, + America__Rio_Branco, + America__Rosario, + America__Santa_Isabel, + America__Santarem, + America__Santiago, + America__Santo_Domingo, + America__Sao_Paulo, + America__Scoresbysund, + America__Shiprock, + America__Sitka, + America__St_Barthelemy, + America__St_Johns, + America__St_Kitts, + America__St_Lucia, + America__St_Thomas, + America__St_Vincent, + America__Swift_Current, + America__Tegucigalpa, + America__Thule, + America__Thunder_Bay, + America__Tijuana, + America__Toronto, + America__Tortola, + America__Vancouver, + America__Virgin, + America__Whitehorse, + America__Winnipeg, + America__Yakutat, + America__Yellowknife, + Antarctica__Casey, + Antarctica__Davis, + Antarctica__DumontDUrville, + Antarctica__Macquarie, + Antarctica__Mawson, + Antarctica__McMurdo, + Antarctica__Palmer, + Antarctica__Rothera, + Antarctica__South_Pole, + Antarctica__Syowa, + Antarctica__Troll, + Antarctica__Vostok, + Arctic__Longyearbyen, + Asia__Aden, + Asia__Almaty, + Asia__Amman, + Asia__Anadyr, + Asia__Aqtau, + Asia__Aqtobe, + Asia__Ashgabat, + Asia__Ashkhabad, + Asia__Atyrau, + Asia__Baghdad, + Asia__Bahrain, + Asia__Baku, + Asia__Bangkok, + Asia__Barnaul, + Asia__Beirut, + Asia__Bishkek, + Asia__Brunei, + Asia__Calcutta, + Asia__Chita, + Asia__Choibalsan, + Asia__Chongqing, + Asia__Chungking, + Asia__Colombo, + Asia__Dacca, + Asia__Damascus, + Asia__Dhaka, + Asia__Dili, + Asia__Dubai, + Asia__Dushanbe, + Asia__Famagusta, + Asia__Gaza, + Asia__Harbin, + Asia__Hebron, + Asia__Ho_Chi_Minh, + Asia__Hong_Kong, + Asia__Hovd, + Asia__Irkutsk, + Asia__Istanbul, + Asia__Jakarta, + Asia__Jayapura, + Asia__Jerusalem, + Asia__Kabul, + Asia__Kamchatka, + Asia__Karachi, + Asia__Kashgar, + Asia__Kathmandu, + Asia__Katmandu, + Asia__Khandyga, + Asia__Kolkata, + Asia__Krasnoyarsk, + Asia__Kuala_Lumpur, + Asia__Kuching, + Asia__Kuwait, + Asia__Macao, + Asia__Macau, + Asia__Magadan, + Asia__Makassar, + Asia__Manila, + Asia__Muscat, + Asia__Nicosia, + Asia__Novokuznetsk, + Asia__Novosibirsk, + Asia__Omsk, + Asia__Oral, + Asia__Phnom_Penh, + Asia__Pontianak, + Asia__Pyongyang, + Asia__Qatar, + Asia__Qostanay, + Asia__Qyzylorda, + Asia__Rangoon, + Asia__Riyadh, + Asia__Saigon, + Asia__Sakhalin, + Asia__Samarkand, + Asia__Seoul, + Asia__Shanghai, + Asia__Singapore, + Asia__Srednekolymsk, + Asia__Taipei, + Asia__Tashkent, + Asia__Tbilisi, + Asia__Tehran, + Asia__Tel_Aviv, + Asia__Thimbu, + Asia__Thimphu, + Asia__Tokyo, + Asia__Tomsk, + Asia__Ujung_Pandang, + Asia__Ulaanbaatar, + Asia__Ulan_Bator, + Asia__Urumqi, + Asia__UstNera, + Asia__Vientiane, + Asia__Vladivostok, + Asia__Yakutsk, + Asia__Yangon, + Asia__Yekaterinburg, + Asia__Yerevan, + Atlantic__Azores, + Atlantic__Bermuda, + Atlantic__Canary, + Atlantic__Cape_Verde, + Atlantic__Faeroe, + Atlantic__Faroe, + Atlantic__Jan_Mayen, + Atlantic__Madeira, + Atlantic__Reykjavik, + Atlantic__South_Georgia, + Atlantic__St_Helena, + Atlantic__Stanley, + Australia__ACT, + Australia__Adelaide, + Australia__Brisbane, + Australia__Broken_Hill, + Australia__Canberra, + Australia__Currie, + Australia__Darwin, + Australia__Eucla, + Australia__Hobart, + Australia__LHI, + Australia__Lindeman, + Australia__Lord_Howe, + Australia__Melbourne, + Australia__NSW, + Australia__North, + Australia__Perth, + Australia__Queensland, + Australia__South, + Australia__Sydney, + Australia__Tasmania, + Australia__Victoria, + Australia__West, + Australia__Yancowinna, + Brazil__Acre, + Brazil__DeNoronha, + Brazil__East, + Brazil__West, + CET, + CST6CDT, + Canada__Atlantic, + Canada__Central, + Canada__Eastern, + Canada__Mountain, + Canada__Newfoundland, + Canada__Pacific, + Canada__Saskatchewan, + Canada__Yukon, + Chile__Continental, + Chile__EasterIsland, + Cuba, + EET, + EST, + EST5EDT, + Egypt, + Eire, + Etc__GMT, + Etc__GMTPlus0, + Etc__GMTPlus1, + Etc__GMTPlus10, + Etc__GMTPlus11, + Etc__GMTPlus12, + Etc__GMTPlus2, + Etc__GMTPlus3, + Etc__GMTPlus4, + Etc__GMTPlus5, + Etc__GMTPlus6, + Etc__GMTPlus7, + Etc__GMTPlus8, + Etc__GMTPlus9, + Etc__GMTMinus0, + Etc__GMTMinus1, + Etc__GMTMinus10, + Etc__GMTMinus11, + Etc__GMTMinus12, + Etc__GMTMinus13, + Etc__GMTMinus14, + Etc__GMTMinus2, + Etc__GMTMinus3, + Etc__GMTMinus4, + Etc__GMTMinus5, + Etc__GMTMinus6, + Etc__GMTMinus7, + Etc__GMTMinus8, + Etc__GMTMinus9, + Etc__GMT0, + Etc__Greenwich, + Etc__UCT, + Etc__UTC, + Etc__Universal, + Etc__Zulu, + Europe__Amsterdam, + Europe__Andorra, + Europe__Astrakhan, + Europe__Athens, + Europe__Belfast, + Europe__Belgrade, + Europe__Berlin, + Europe__Bratislava, + Europe__Brussels, + Europe__Bucharest, + Europe__Budapest, + Europe__Busingen, + Europe__Chisinau, + Europe__Copenhagen, + Europe__Dublin, + Europe__Gibraltar, + Europe__Guernsey, + Europe__Helsinki, + Europe__Isle_of_Man, + Europe__Istanbul, + Europe__Jersey, + Europe__Kaliningrad, + Europe__Kiev, + Europe__Kirov, + Europe__Kyiv, + Europe__Lisbon, + Europe__Ljubljana, + Europe__London, + Europe__Luxembourg, + Europe__Madrid, + Europe__Malta, + Europe__Mariehamn, + Europe__Minsk, + Europe__Monaco, + Europe__Moscow, + Europe__Nicosia, + Europe__Oslo, + Europe__Paris, + Europe__Podgorica, + Europe__Prague, + Europe__Riga, + Europe__Rome, + Europe__Samara, + Europe__San_Marino, + Europe__Sarajevo, + Europe__Saratov, + Europe__Simferopol, + Europe__Skopje, + Europe__Sofia, + Europe__Stockholm, + Europe__Tallinn, + Europe__Tirane, + Europe__Tiraspol, + Europe__Ulyanovsk, + Europe__Uzhgorod, + Europe__Vaduz, + Europe__Vatican, + Europe__Vienna, + Europe__Vilnius, + Europe__Volgograd, + Europe__Warsaw, + Europe__Zagreb, + Europe__Zaporozhye, + Europe__Zurich, + GB, + GBEire, + GMT, + GMTPlus0, + GMTMinus0, + GMT0, + Greenwich, + HST, + Hongkong, + Iceland, + Indian__Antananarivo, + Indian__Chagos, + Indian__Christmas, + Indian__Cocos, + Indian__Comoro, + Indian__Kerguelen, + Indian__Mahe, + Indian__Maldives, + Indian__Mauritius, + Indian__Mayotte, + Indian__Reunion, + Iran, + Israel, + Jamaica, + Japan, + Kwajalein, + Libya, + MET, + MST, + MST7MDT, + Mexico__BajaNorte, + Mexico__BajaSur, + Mexico__General, + NZ, + NZCHAT, + Navajo, + PRC, + PST8PDT, + Pacific__Apia, + Pacific__Auckland, + Pacific__Bougainville, + Pacific__Chatham, + Pacific__Chuuk, + Pacific__Easter, + Pacific__Efate, + Pacific__Enderbury, + Pacific__Fakaofo, + Pacific__Fiji, + Pacific__Funafuti, + Pacific__Galapagos, + Pacific__Gambier, + Pacific__Guadalcanal, + Pacific__Guam, + Pacific__Honolulu, + Pacific__Johnston, + Pacific__Kanton, + Pacific__Kiritimati, + Pacific__Kosrae, + Pacific__Kwajalein, + Pacific__Majuro, + Pacific__Marquesas, + Pacific__Midway, + Pacific__Nauru, + Pacific__Niue, + Pacific__Norfolk, + Pacific__Noumea, + Pacific__Pago_Pago, + Pacific__Palau, + Pacific__Pitcairn, + Pacific__Pohnpei, + Pacific__Ponape, + Pacific__Port_Moresby, + Pacific__Rarotonga, + Pacific__Saipan, + Pacific__Samoa, + Pacific__Tahiti, + Pacific__Tarawa, + Pacific__Tongatapu, + Pacific__Truk, + Pacific__Wake, + Pacific__Wallis, + Pacific__Yap, + Poland, + Portugal, + ROC, + ROK, + Singapore, + Turkey, + UCT, + US__Alaska, + US__Aleutian, + US__Arizona, + US__Central, + US__EastIndiana, + US__Eastern, + US__Hawaii, + US__IndianaStarke, + US__Michigan, + US__Mountain, + US__Pacific, + US__Samoa, + UTC, + Universal, + WSU, + WET, + Zulu, +
}
Expand description

TimeZones built at compile time from the tz database

+

This implements chrono::TimeZone so that it may be used in and to +construct chrono’s DateTime type. See the root module documentation +for details.

+

Variants§

§

Africa__Abidjan

Africa/Abidjan

+
§

Africa__Accra

Africa/Accra

+
§

Africa__Addis_Ababa

Africa/Addis_Ababa

+
§

Africa__Algiers

Africa/Algiers

+
§

Africa__Asmara

Africa/Asmara

+
§

Africa__Asmera

Africa/Asmera

+
§

Africa__Bamako

Africa/Bamako

+
§

Africa__Bangui

Africa/Bangui

+
§

Africa__Banjul

Africa/Banjul

+
§

Africa__Bissau

Africa/Bissau

+
§

Africa__Blantyre

Africa/Blantyre

+
§

Africa__Brazzaville

Africa/Brazzaville

+
§

Africa__Bujumbura

Africa/Bujumbura

+
§

Africa__Cairo

Africa/Cairo

+
§

Africa__Casablanca

Africa/Casablanca

+
§

Africa__Ceuta

Africa/Ceuta

+
§

Africa__Conakry

Africa/Conakry

+
§

Africa__Dakar

Africa/Dakar

+
§

Africa__Dar_es_Salaam

Africa/Dar_es_Salaam

+
§

Africa__Djibouti

Africa/Djibouti

+
§

Africa__Douala

Africa/Douala

+
§

Africa__El_Aaiun

Africa/El_Aaiun

+
§

Africa__Freetown

Africa/Freetown

+
§

Africa__Gaborone

Africa/Gaborone

+
§

Africa__Harare

Africa/Harare

+
§

Africa__Johannesburg

Africa/Johannesburg

+
§

Africa__Juba

Africa/Juba

+
§

Africa__Kampala

Africa/Kampala

+
§

Africa__Khartoum

Africa/Khartoum

+
§

Africa__Kigali

Africa/Kigali

+
§

Africa__Kinshasa

Africa/Kinshasa

+
§

Africa__Lagos

Africa/Lagos

+
§

Africa__Libreville

Africa/Libreville

+
§

Africa__Lome

Africa/Lome

+
§

Africa__Luanda

Africa/Luanda

+
§

Africa__Lubumbashi

Africa/Lubumbashi

+
§

Africa__Lusaka

Africa/Lusaka

+
§

Africa__Malabo

Africa/Malabo

+
§

Africa__Maputo

Africa/Maputo

+
§

Africa__Maseru

Africa/Maseru

+
§

Africa__Mbabane

Africa/Mbabane

+
§

Africa__Mogadishu

Africa/Mogadishu

+
§

Africa__Monrovia

Africa/Monrovia

+
§

Africa__Nairobi

Africa/Nairobi

+
§

Africa__Ndjamena

Africa/Ndjamena

+
§

Africa__Niamey

Africa/Niamey

+
§

Africa__Nouakchott

Africa/Nouakchott

+
§

Africa__Ouagadougou

Africa/Ouagadougou

+
§

Africa__PortoNovo

Africa/Porto-Novo

+
§

Africa__Sao_Tome

Africa/Sao_Tome

+
§

Africa__Timbuktu

Africa/Timbuktu

+
§

Africa__Tripoli

Africa/Tripoli

+
§

Africa__Tunis

Africa/Tunis

+
§

Africa__Windhoek

Africa/Windhoek

+
§

America__Adak

America/Adak

+
§

America__Anchorage

America/Anchorage

+
§

America__Anguilla

America/Anguilla

+
§

America__Antigua

America/Antigua

+
§

America__Araguaina

America/Araguaina

+
§

America__Argentina__Buenos_Aires

America/Argentina/Buenos_Aires

+
§

America__Argentina__Catamarca

America/Argentina/Catamarca

+
§

America__Argentina__ComodRivadavia

America/Argentina/ComodRivadavia

+
§

America__Argentina__Cordoba

America/Argentina/Cordoba

+
§

America__Argentina__Jujuy

America/Argentina/Jujuy

+
§

America__Argentina__La_Rioja

America/Argentina/La_Rioja

+
§

America__Argentina__Mendoza

America/Argentina/Mendoza

+
§

America__Argentina__Rio_Gallegos

America/Argentina/Rio_Gallegos

+
§

America__Argentina__Salta

America/Argentina/Salta

+
§

America__Argentina__San_Juan

America/Argentina/San_Juan

+
§

America__Argentina__San_Luis

America/Argentina/San_Luis

+
§

America__Argentina__Tucuman

America/Argentina/Tucuman

+
§

America__Argentina__Ushuaia

America/Argentina/Ushuaia

+
§

America__Aruba

America/Aruba

+
§

America__Asuncion

America/Asuncion

+
§

America__Atikokan

America/Atikokan

+
§

America__Atka

America/Atka

+
§

America__Bahia

America/Bahia

+
§

America__Bahia_Banderas

America/Bahia_Banderas

+
§

America__Barbados

America/Barbados

+
§

America__Belem

America/Belem

+
§

America__Belize

America/Belize

+
§

America__BlancSablon

America/Blanc-Sablon

+
§

America__Boa_Vista

America/Boa_Vista

+
§

America__Bogota

America/Bogota

+
§

America__Boise

America/Boise

+
§

America__Buenos_Aires

America/Buenos_Aires

+
§

America__Cambridge_Bay

America/Cambridge_Bay

+
§

America__Campo_Grande

America/Campo_Grande

+
§

America__Cancun

America/Cancun

+
§

America__Caracas

America/Caracas

+
§

America__Catamarca

America/Catamarca

+
§

America__Cayenne

America/Cayenne

+
§

America__Cayman

America/Cayman

+
§

America__Chicago

America/Chicago

+
§

America__Chihuahua

America/Chihuahua

+
§

America__Coral_Harbour

America/Coral_Harbour

+
§

America__Cordoba

America/Cordoba

+
§

America__Costa_Rica

America/Costa_Rica

+
§

America__Creston

America/Creston

+
§

America__Cuiaba

America/Cuiaba

+
§

America__Curacao

America/Curacao

+
§

America__Danmarkshavn

America/Danmarkshavn

+
§

America__Dawson

America/Dawson

+
§

America__Dawson_Creek

America/Dawson_Creek

+
§

America__Denver

America/Denver

+
§

America__Detroit

America/Detroit

+
§

America__Dominica

America/Dominica

+
§

America__Edmonton

America/Edmonton

+
§

America__Eirunepe

America/Eirunepe

+
§

America__El_Salvador

America/El_Salvador

+
§

America__Ensenada

America/Ensenada

+
§

America__Fort_Nelson

America/Fort_Nelson

+
§

America__Fort_Wayne

America/Fort_Wayne

+
§

America__Fortaleza

America/Fortaleza

+
§

America__Glace_Bay

America/Glace_Bay

+
§

America__Godthab

America/Godthab

+
§

America__Goose_Bay

America/Goose_Bay

+
§

America__Grand_Turk

America/Grand_Turk

+
§

America__Grenada

America/Grenada

+
§

America__Guadeloupe

America/Guadeloupe

+
§

America__Guatemala

America/Guatemala

+
§

America__Guayaquil

America/Guayaquil

+
§

America__Guyana

America/Guyana

+
§

America__Halifax

America/Halifax

+
§

America__Havana

America/Havana

+
§

America__Hermosillo

America/Hermosillo

+
§

America__Indiana__Indianapolis

America/Indiana/Indianapolis

+
§

America__Indiana__Knox

America/Indiana/Knox

+
§

America__Indiana__Marengo

America/Indiana/Marengo

+
§

America__Indiana__Petersburg

America/Indiana/Petersburg

+
§

America__Indiana__Tell_City

America/Indiana/Tell_City

+
§

America__Indiana__Vevay

America/Indiana/Vevay

+
§

America__Indiana__Vincennes

America/Indiana/Vincennes

+
§

America__Indiana__Winamac

America/Indiana/Winamac

+
§

America__Indianapolis

America/Indianapolis

+
§

America__Inuvik

America/Inuvik

+
§

America__Iqaluit

America/Iqaluit

+
§

America__Jamaica

America/Jamaica

+
§

America__Jujuy

America/Jujuy

+
§

America__Juneau

America/Juneau

+
§

America__Kentucky__Louisville

America/Kentucky/Louisville

+
§

America__Kentucky__Monticello

America/Kentucky/Monticello

+
§

America__Knox_IN

America/Knox_IN

+
§

America__Kralendijk

America/Kralendijk

+
§

America__La_Paz

America/La_Paz

+
§

America__Lima

America/Lima

+
§

America__Los_Angeles

America/Los_Angeles

+
§

America__Louisville

America/Louisville

+
§

America__Lower_Princes

America/Lower_Princes

+
§

America__Maceio

America/Maceio

+
§

America__Managua

America/Managua

+
§

America__Manaus

America/Manaus

+
§

America__Marigot

America/Marigot

+
§

America__Martinique

America/Martinique

+
§

America__Matamoros

America/Matamoros

+
§

America__Mazatlan

America/Mazatlan

+
§

America__Mendoza

America/Mendoza

+
§

America__Menominee

America/Menominee

+
§

America__Merida

America/Merida

+
§

America__Metlakatla

America/Metlakatla

+
§

America__Mexico_City

America/Mexico_City

+
§

America__Miquelon

America/Miquelon

+
§

America__Moncton

America/Moncton

+
§

America__Monterrey

America/Monterrey

+
§

America__Montevideo

America/Montevideo

+
§

America__Montreal

America/Montreal

+
§

America__Montserrat

America/Montserrat

+
§

America__Nassau

America/Nassau

+
§

America__New_York

America/New_York

+
§

America__Nipigon

America/Nipigon

+
§

America__Nome

America/Nome

+
§

America__Noronha

America/Noronha

+
§

America__North_Dakota__Beulah

America/North_Dakota/Beulah

+
§

America__North_Dakota__Center

America/North_Dakota/Center

+
§

America__North_Dakota__New_Salem

America/North_Dakota/New_Salem

+
§

America__Nuuk

America/Nuuk

+
§

America__Ojinaga

America/Ojinaga

+
§

America__Panama

America/Panama

+
§

America__Pangnirtung

America/Pangnirtung

+
§

America__Paramaribo

America/Paramaribo

+
§

America__Phoenix

America/Phoenix

+
§

America__PortauPrince

America/Port-au-Prince

+
§

America__Port_of_Spain

America/Port_of_Spain

+
§

America__Porto_Acre

America/Porto_Acre

+
§

America__Porto_Velho

America/Porto_Velho

+
§

America__Puerto_Rico

America/Puerto_Rico

+
§

America__Punta_Arenas

America/Punta_Arenas

+
§

America__Rainy_River

America/Rainy_River

+
§

America__Rankin_Inlet

America/Rankin_Inlet

+
§

America__Recife

America/Recife

+
§

America__Regina

America/Regina

+
§

America__Resolute

America/Resolute

+
§

America__Rio_Branco

America/Rio_Branco

+
§

America__Rosario

America/Rosario

+
§

America__Santa_Isabel

America/Santa_Isabel

+
§

America__Santarem

America/Santarem

+
§

America__Santiago

America/Santiago

+
§

America__Santo_Domingo

America/Santo_Domingo

+
§

America__Sao_Paulo

America/Sao_Paulo

+
§

America__Scoresbysund

America/Scoresbysund

+
§

America__Shiprock

America/Shiprock

+
§

America__Sitka

America/Sitka

+
§

America__St_Barthelemy

America/St_Barthelemy

+
§

America__St_Johns

America/St_Johns

+
§

America__St_Kitts

America/St_Kitts

+
§

America__St_Lucia

America/St_Lucia

+
§

America__St_Thomas

America/St_Thomas

+
§

America__St_Vincent

America/St_Vincent

+
§

America__Swift_Current

America/Swift_Current

+
§

America__Tegucigalpa

America/Tegucigalpa

+
§

America__Thule

America/Thule

+
§

America__Thunder_Bay

America/Thunder_Bay

+
§

America__Tijuana

America/Tijuana

+
§

America__Toronto

America/Toronto

+
§

America__Tortola

America/Tortola

+
§

America__Vancouver

America/Vancouver

+
§

America__Virgin

America/Virgin

+
§

America__Whitehorse

America/Whitehorse

+
§

America__Winnipeg

America/Winnipeg

+
§

America__Yakutat

America/Yakutat

+
§

America__Yellowknife

America/Yellowknife

+
§

Antarctica__Casey

Antarctica/Casey

+
§

Antarctica__Davis

Antarctica/Davis

+
§

Antarctica__DumontDUrville

Antarctica/DumontDUrville

+
§

Antarctica__Macquarie

Antarctica/Macquarie

+
§

Antarctica__Mawson

Antarctica/Mawson

+
§

Antarctica__McMurdo

Antarctica/McMurdo

+
§

Antarctica__Palmer

Antarctica/Palmer

+
§

Antarctica__Rothera

Antarctica/Rothera

+
§

Antarctica__South_Pole

Antarctica/South_Pole

+
§

Antarctica__Syowa

Antarctica/Syowa

+
§

Antarctica__Troll

Antarctica/Troll

+
§

Antarctica__Vostok

Antarctica/Vostok

+
§

Arctic__Longyearbyen

Arctic/Longyearbyen

+
§

Asia__Aden

Asia/Aden

+
§

Asia__Almaty

Asia/Almaty

+
§

Asia__Amman

Asia/Amman

+
§

Asia__Anadyr

Asia/Anadyr

+
§

Asia__Aqtau

Asia/Aqtau

+
§

Asia__Aqtobe

Asia/Aqtobe

+
§

Asia__Ashgabat

Asia/Ashgabat

+
§

Asia__Ashkhabad

Asia/Ashkhabad

+
§

Asia__Atyrau

Asia/Atyrau

+
§

Asia__Baghdad

Asia/Baghdad

+
§

Asia__Bahrain

Asia/Bahrain

+
§

Asia__Baku

Asia/Baku

+
§

Asia__Bangkok

Asia/Bangkok

+
§

Asia__Barnaul

Asia/Barnaul

+
§

Asia__Beirut

Asia/Beirut

+
§

Asia__Bishkek

Asia/Bishkek

+
§

Asia__Brunei

Asia/Brunei

+
§

Asia__Calcutta

Asia/Calcutta

+
§

Asia__Chita

Asia/Chita

+
§

Asia__Choibalsan

Asia/Choibalsan

+
§

Asia__Chongqing

Asia/Chongqing

+
§

Asia__Chungking

Asia/Chungking

+
§

Asia__Colombo

Asia/Colombo

+
§

Asia__Dacca

Asia/Dacca

+
§

Asia__Damascus

Asia/Damascus

+
§

Asia__Dhaka

Asia/Dhaka

+
§

Asia__Dili

Asia/Dili

+
§

Asia__Dubai

Asia/Dubai

+
§

Asia__Dushanbe

Asia/Dushanbe

+
§

Asia__Famagusta

Asia/Famagusta

+
§

Asia__Gaza

Asia/Gaza

+
§

Asia__Harbin

Asia/Harbin

+
§

Asia__Hebron

Asia/Hebron

+
§

Asia__Ho_Chi_Minh

Asia/Ho_Chi_Minh

+
§

Asia__Hong_Kong

Asia/Hong_Kong

+
§

Asia__Hovd

Asia/Hovd

+
§

Asia__Irkutsk

Asia/Irkutsk

+
§

Asia__Istanbul

Asia/Istanbul

+
§

Asia__Jakarta

Asia/Jakarta

+
§

Asia__Jayapura

Asia/Jayapura

+
§

Asia__Jerusalem

Asia/Jerusalem

+
§

Asia__Kabul

Asia/Kabul

+
§

Asia__Kamchatka

Asia/Kamchatka

+
§

Asia__Karachi

Asia/Karachi

+
§

Asia__Kashgar

Asia/Kashgar

+
§

Asia__Kathmandu

Asia/Kathmandu

+
§

Asia__Katmandu

Asia/Katmandu

+
§

Asia__Khandyga

Asia/Khandyga

+
§

Asia__Kolkata

Asia/Kolkata

+
§

Asia__Krasnoyarsk

Asia/Krasnoyarsk

+
§

Asia__Kuala_Lumpur

Asia/Kuala_Lumpur

+
§

Asia__Kuching

Asia/Kuching

+
§

Asia__Kuwait

Asia/Kuwait

+
§

Asia__Macao

Asia/Macao

+
§

Asia__Macau

Asia/Macau

+
§

Asia__Magadan

Asia/Magadan

+
§

Asia__Makassar

Asia/Makassar

+
§

Asia__Manila

Asia/Manila

+
§

Asia__Muscat

Asia/Muscat

+
§

Asia__Nicosia

Asia/Nicosia

+
§

Asia__Novokuznetsk

Asia/Novokuznetsk

+
§

Asia__Novosibirsk

Asia/Novosibirsk

+
§

Asia__Omsk

Asia/Omsk

+
§

Asia__Oral

Asia/Oral

+
§

Asia__Phnom_Penh

Asia/Phnom_Penh

+
§

Asia__Pontianak

Asia/Pontianak

+
§

Asia__Pyongyang

Asia/Pyongyang

+
§

Asia__Qatar

Asia/Qatar

+
§

Asia__Qostanay

Asia/Qostanay

+
§

Asia__Qyzylorda

Asia/Qyzylorda

+
§

Asia__Rangoon

Asia/Rangoon

+
§

Asia__Riyadh

Asia/Riyadh

+
§

Asia__Saigon

Asia/Saigon

+
§

Asia__Sakhalin

Asia/Sakhalin

+
§

Asia__Samarkand

Asia/Samarkand

+
§

Asia__Seoul

Asia/Seoul

+
§

Asia__Shanghai

Asia/Shanghai

+
§

Asia__Singapore

Asia/Singapore

+
§

Asia__Srednekolymsk

Asia/Srednekolymsk

+
§

Asia__Taipei

Asia/Taipei

+
§

Asia__Tashkent

Asia/Tashkent

+
§

Asia__Tbilisi

Asia/Tbilisi

+
§

Asia__Tehran

Asia/Tehran

+
§

Asia__Tel_Aviv

Asia/Tel_Aviv

+
§

Asia__Thimbu

Asia/Thimbu

+
§

Asia__Thimphu

Asia/Thimphu

+
§

Asia__Tokyo

Asia/Tokyo

+
§

Asia__Tomsk

Asia/Tomsk

+
§

Asia__Ujung_Pandang

Asia/Ujung_Pandang

+
§

Asia__Ulaanbaatar

Asia/Ulaanbaatar

+
§

Asia__Ulan_Bator

Asia/Ulan_Bator

+
§

Asia__Urumqi

Asia/Urumqi

+
§

Asia__UstNera

Asia/Ust-Nera

+
§

Asia__Vientiane

Asia/Vientiane

+
§

Asia__Vladivostok

Asia/Vladivostok

+
§

Asia__Yakutsk

Asia/Yakutsk

+
§

Asia__Yangon

Asia/Yangon

+
§

Asia__Yekaterinburg

Asia/Yekaterinburg

+
§

Asia__Yerevan

Asia/Yerevan

+
§

Atlantic__Azores

Atlantic/Azores

+
§

Atlantic__Bermuda

Atlantic/Bermuda

+
§

Atlantic__Canary

Atlantic/Canary

+
§

Atlantic__Cape_Verde

Atlantic/Cape_Verde

+
§

Atlantic__Faeroe

Atlantic/Faeroe

+
§

Atlantic__Faroe

Atlantic/Faroe

+
§

Atlantic__Jan_Mayen

Atlantic/Jan_Mayen

+
§

Atlantic__Madeira

Atlantic/Madeira

+
§

Atlantic__Reykjavik

Atlantic/Reykjavik

+
§

Atlantic__South_Georgia

Atlantic/South_Georgia

+
§

Atlantic__St_Helena

Atlantic/St_Helena

+
§

Atlantic__Stanley

Atlantic/Stanley

+
§

Australia__ACT

Australia/ACT

+
§

Australia__Adelaide

Australia/Adelaide

+
§

Australia__Brisbane

Australia/Brisbane

+
§

Australia__Broken_Hill

Australia/Broken_Hill

+
§

Australia__Canberra

Australia/Canberra

+
§

Australia__Currie

Australia/Currie

+
§

Australia__Darwin

Australia/Darwin

+
§

Australia__Eucla

Australia/Eucla

+
§

Australia__Hobart

Australia/Hobart

+
§

Australia__LHI

Australia/LHI

+
§

Australia__Lindeman

Australia/Lindeman

+
§

Australia__Lord_Howe

Australia/Lord_Howe

+
§

Australia__Melbourne

Australia/Melbourne

+
§

Australia__NSW

Australia/NSW

+
§

Australia__North

Australia/North

+
§

Australia__Perth

Australia/Perth

+
§

Australia__Queensland

Australia/Queensland

+
§

Australia__South

Australia/South

+
§

Australia__Sydney

Australia/Sydney

+
§

Australia__Tasmania

Australia/Tasmania

+
§

Australia__Victoria

Australia/Victoria

+
§

Australia__West

Australia/West

+
§

Australia__Yancowinna

Australia/Yancowinna

+
§

Brazil__Acre

Brazil/Acre

+
§

Brazil__DeNoronha

Brazil/DeNoronha

+
§

Brazil__East

Brazil/East

+
§

Brazil__West

Brazil/West

+
§

CET

CET

+
§

CST6CDT

CST6CDT

+
§

Canada__Atlantic

Canada/Atlantic

+
§

Canada__Central

Canada/Central

+
§

Canada__Eastern

Canada/Eastern

+
§

Canada__Mountain

Canada/Mountain

+
§

Canada__Newfoundland

Canada/Newfoundland

+
§

Canada__Pacific

Canada/Pacific

+
§

Canada__Saskatchewan

Canada/Saskatchewan

+
§

Canada__Yukon

Canada/Yukon

+
§

Chile__Continental

Chile/Continental

+
§

Chile__EasterIsland

Chile/EasterIsland

+
§

Cuba

Cuba

+
§

EET

EET

+
§

EST

EST

+
§

EST5EDT

EST5EDT

+
§

Egypt

Egypt

+
§

Eire

Eire

+
§

Etc__GMT

Etc/GMT

+
§

Etc__GMTPlus0

Etc/GMT+0

+
§

Etc__GMTPlus1

Etc/GMT+1

+
§

Etc__GMTPlus10

Etc/GMT+10

+
§

Etc__GMTPlus11

Etc/GMT+11

+
§

Etc__GMTPlus12

Etc/GMT+12

+
§

Etc__GMTPlus2

Etc/GMT+2

+
§

Etc__GMTPlus3

Etc/GMT+3

+
§

Etc__GMTPlus4

Etc/GMT+4

+
§

Etc__GMTPlus5

Etc/GMT+5

+
§

Etc__GMTPlus6

Etc/GMT+6

+
§

Etc__GMTPlus7

Etc/GMT+7

+
§

Etc__GMTPlus8

Etc/GMT+8

+
§

Etc__GMTPlus9

Etc/GMT+9

+
§

Etc__GMTMinus0

Etc/GMT-0

+
§

Etc__GMTMinus1

Etc/GMT-1

+
§

Etc__GMTMinus10

Etc/GMT-10

+
§

Etc__GMTMinus11

Etc/GMT-11

+
§

Etc__GMTMinus12

Etc/GMT-12

+
§

Etc__GMTMinus13

Etc/GMT-13

+
§

Etc__GMTMinus14

Etc/GMT-14

+
§

Etc__GMTMinus2

Etc/GMT-2

+
§

Etc__GMTMinus3

Etc/GMT-3

+
§

Etc__GMTMinus4

Etc/GMT-4

+
§

Etc__GMTMinus5

Etc/GMT-5

+
§

Etc__GMTMinus6

Etc/GMT-6

+
§

Etc__GMTMinus7

Etc/GMT-7

+
§

Etc__GMTMinus8

Etc/GMT-8

+
§

Etc__GMTMinus9

Etc/GMT-9

+
§

Etc__GMT0

Etc/GMT0

+
§

Etc__Greenwich

Etc/Greenwich

+
§

Etc__UCT

Etc/UCT

+
§

Etc__UTC

Etc/UTC

+
§

Etc__Universal

Etc/Universal

+
§

Etc__Zulu

Etc/Zulu

+
§

Europe__Amsterdam

Europe/Amsterdam

+
§

Europe__Andorra

Europe/Andorra

+
§

Europe__Astrakhan

Europe/Astrakhan

+
§

Europe__Athens

Europe/Athens

+
§

Europe__Belfast

Europe/Belfast

+
§

Europe__Belgrade

Europe/Belgrade

+
§

Europe__Berlin

Europe/Berlin

+
§

Europe__Bratislava

Europe/Bratislava

+
§

Europe__Brussels

Europe/Brussels

+
§

Europe__Bucharest

Europe/Bucharest

+
§

Europe__Budapest

Europe/Budapest

+
§

Europe__Busingen

Europe/Busingen

+
§

Europe__Chisinau

Europe/Chisinau

+
§

Europe__Copenhagen

Europe/Copenhagen

+
§

Europe__Dublin

Europe/Dublin

+
§

Europe__Gibraltar

Europe/Gibraltar

+
§

Europe__Guernsey

Europe/Guernsey

+
§

Europe__Helsinki

Europe/Helsinki

+
§

Europe__Isle_of_Man

Europe/Isle_of_Man

+
§

Europe__Istanbul

Europe/Istanbul

+
§

Europe__Jersey

Europe/Jersey

+
§

Europe__Kaliningrad

Europe/Kaliningrad

+
§

Europe__Kiev

Europe/Kiev

+
§

Europe__Kirov

Europe/Kirov

+
§

Europe__Kyiv

Europe/Kyiv

+
§

Europe__Lisbon

Europe/Lisbon

+
§

Europe__Ljubljana

Europe/Ljubljana

+
§

Europe__London

Europe/London

+
§

Europe__Luxembourg

Europe/Luxembourg

+
§

Europe__Madrid

Europe/Madrid

+
§

Europe__Malta

Europe/Malta

+
§

Europe__Mariehamn

Europe/Mariehamn

+
§

Europe__Minsk

Europe/Minsk

+
§

Europe__Monaco

Europe/Monaco

+
§

Europe__Moscow

Europe/Moscow

+
§

Europe__Nicosia

Europe/Nicosia

+
§

Europe__Oslo

Europe/Oslo

+
§

Europe__Paris

Europe/Paris

+
§

Europe__Podgorica

Europe/Podgorica

+
§

Europe__Prague

Europe/Prague

+
§

Europe__Riga

Europe/Riga

+
§

Europe__Rome

Europe/Rome

+
§

Europe__Samara

Europe/Samara

+
§

Europe__San_Marino

Europe/San_Marino

+
§

Europe__Sarajevo

Europe/Sarajevo

+
§

Europe__Saratov

Europe/Saratov

+
§

Europe__Simferopol

Europe/Simferopol

+
§

Europe__Skopje

Europe/Skopje

+
§

Europe__Sofia

Europe/Sofia

+
§

Europe__Stockholm

Europe/Stockholm

+
§

Europe__Tallinn

Europe/Tallinn

+
§

Europe__Tirane

Europe/Tirane

+
§

Europe__Tiraspol

Europe/Tiraspol

+
§

Europe__Ulyanovsk

Europe/Ulyanovsk

+
§

Europe__Uzhgorod

Europe/Uzhgorod

+
§

Europe__Vaduz

Europe/Vaduz

+
§

Europe__Vatican

Europe/Vatican

+
§

Europe__Vienna

Europe/Vienna

+
§

Europe__Vilnius

Europe/Vilnius

+
§

Europe__Volgograd

Europe/Volgograd

+
§

Europe__Warsaw

Europe/Warsaw

+
§

Europe__Zagreb

Europe/Zagreb

+
§

Europe__Zaporozhye

Europe/Zaporozhye

+
§

Europe__Zurich

Europe/Zurich

+
§

GB

GB

+
§

GBEire

GB-Eire

+
§

GMT

GMT

+
§

GMTPlus0

GMT+0

+
§

GMTMinus0

GMT-0

+
§

GMT0

GMT0

+
§

Greenwich

Greenwich

+
§

HST

HST

+
§

Hongkong

Hongkong

+
§

Iceland

Iceland

+
§

Indian__Antananarivo

Indian/Antananarivo

+
§

Indian__Chagos

Indian/Chagos

+
§

Indian__Christmas

Indian/Christmas

+
§

Indian__Cocos

Indian/Cocos

+
§

Indian__Comoro

Indian/Comoro

+
§

Indian__Kerguelen

Indian/Kerguelen

+
§

Indian__Mahe

Indian/Mahe

+
§

Indian__Maldives

Indian/Maldives

+
§

Indian__Mauritius

Indian/Mauritius

+
§

Indian__Mayotte

Indian/Mayotte

+
§

Indian__Reunion

Indian/Reunion

+
§

Iran

Iran

+
§

Israel

Israel

+
§

Jamaica

Jamaica

+
§

Japan

Japan

+
§

Kwajalein

Kwajalein

+
§

Libya

Libya

+
§

MET

MET

+
§

MST

MST

+
§

MST7MDT

MST7MDT

+
§

Mexico__BajaNorte

Mexico/BajaNorte

+
§

Mexico__BajaSur

Mexico/BajaSur

+
§

Mexico__General

Mexico/General

+
§

NZ

NZ

+
§

NZCHAT

NZ-CHAT

+
§

Navajo

Navajo

+
§

PRC

PRC

+
§

PST8PDT

PST8PDT

+
§

Pacific__Apia

Pacific/Apia

+
§

Pacific__Auckland

Pacific/Auckland

+
§

Pacific__Bougainville

Pacific/Bougainville

+
§

Pacific__Chatham

Pacific/Chatham

+
§

Pacific__Chuuk

Pacific/Chuuk

+
§

Pacific__Easter

Pacific/Easter

+
§

Pacific__Efate

Pacific/Efate

+
§

Pacific__Enderbury

Pacific/Enderbury

+
§

Pacific__Fakaofo

Pacific/Fakaofo

+
§

Pacific__Fiji

Pacific/Fiji

+
§

Pacific__Funafuti

Pacific/Funafuti

+
§

Pacific__Galapagos

Pacific/Galapagos

+
§

Pacific__Gambier

Pacific/Gambier

+
§

Pacific__Guadalcanal

Pacific/Guadalcanal

+
§

Pacific__Guam

Pacific/Guam

+
§

Pacific__Honolulu

Pacific/Honolulu

+
§

Pacific__Johnston

Pacific/Johnston

+
§

Pacific__Kanton

Pacific/Kanton

+
§

Pacific__Kiritimati

Pacific/Kiritimati

+
§

Pacific__Kosrae

Pacific/Kosrae

+
§

Pacific__Kwajalein

Pacific/Kwajalein

+
§

Pacific__Majuro

Pacific/Majuro

+
§

Pacific__Marquesas

Pacific/Marquesas

+
§

Pacific__Midway

Pacific/Midway

+
§

Pacific__Nauru

Pacific/Nauru

+
§

Pacific__Niue

Pacific/Niue

+
§

Pacific__Norfolk

Pacific/Norfolk

+
§

Pacific__Noumea

Pacific/Noumea

+
§

Pacific__Pago_Pago

Pacific/Pago_Pago

+
§

Pacific__Palau

Pacific/Palau

+
§

Pacific__Pitcairn

Pacific/Pitcairn

+
§

Pacific__Pohnpei

Pacific/Pohnpei

+
§

Pacific__Ponape

Pacific/Ponape

+
§

Pacific__Port_Moresby

Pacific/Port_Moresby

+
§

Pacific__Rarotonga

Pacific/Rarotonga

+
§

Pacific__Saipan

Pacific/Saipan

+
§

Pacific__Samoa

Pacific/Samoa

+
§

Pacific__Tahiti

Pacific/Tahiti

+
§

Pacific__Tarawa

Pacific/Tarawa

+
§

Pacific__Tongatapu

Pacific/Tongatapu

+
§

Pacific__Truk

Pacific/Truk

+
§

Pacific__Wake

Pacific/Wake

+
§

Pacific__Wallis

Pacific/Wallis

+
§

Pacific__Yap

Pacific/Yap

+
§

Poland

Poland

+
§

Portugal

Portugal

+
§

ROC

ROC

+
§

ROK

ROK

+
§

Singapore

Singapore

+
§

Turkey

Turkey

+
§

UCT

UCT

+
§

US__Alaska

US/Alaska

+
§

US__Aleutian

US/Aleutian

+
§

US__Arizona

US/Arizona

+
§

US__Central

US/Central

+
§

US__EastIndiana

US/East-Indiana

+
§

US__Eastern

US/Eastern

+
§

US__Hawaii

US/Hawaii

+
§

US__IndianaStarke

US/Indiana-Starke

+
§

US__Michigan

US/Michigan

+
§

US__Mountain

US/Mountain

+
§

US__Pacific

US/Pacific

+
§

US__Samoa

US/Samoa

+
§

UTC

UTC

+
§

Universal

Universal

+
§

WSU

W-SU

+
§

WET

WET

+
§

Zulu

Zulu

+

Implementations§

§

impl Tz

pub fn name(self) -> &'static str

Trait Implementations§

§

impl Clone for Tz

§

fn clone(&self) -> Tz

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for Tz

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl Display for Tz

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl FromStr for Tz

§

type Err = String

The associated error which can be returned from parsing.
§

fn from_str(s: &str) -> Result<Tz, <Tz as FromStr>::Err>

Parses a string s to return a value of this type. Read more
§

impl Hash for Tz

§

fn hash<__H>(&self, state: &mut __H)where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
§

impl PartialEq for Tz

§

fn eq(&self, other: &Tz) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
§

impl TimeZone for Tz

§

type Offset = TzOffset

An associated offset type. +This type is used to store the actual offset in date and time types. +The original TimeZone value can be recovered via TimeZone::from_offset.
§

fn from_offset(offset: &<Tz as TimeZone>::Offset) -> Tz

Reconstructs the time zone from the offset.
§

fn offset_from_local_date( + &self, + local: &NaiveDate +) -> LocalResult<<Tz as TimeZone>::Offset>

Creates the offset(s) for given local NaiveDate if possible.
§

fn offset_from_local_datetime( + &self, + local: &NaiveDateTime +) -> LocalResult<<Tz as TimeZone>::Offset>

Creates the offset(s) for given local NaiveDateTime if possible.
§

fn offset_from_utc_date(&self, utc: &NaiveDate) -> <Tz as TimeZone>::Offset

Creates the offset for given UTC NaiveDate. This cannot fail.
§

fn offset_from_utc_datetime( + &self, + utc: &NaiveDateTime +) -> <Tz as TimeZone>::Offset

Creates the offset for given UTC NaiveDateTime. This cannot fail.
source§

fn with_ymd_and_hms( + &self, + year: i32, + month: u32, + day: u32, + hour: u32, + min: u32, + sec: u32 +) -> LocalResult<DateTime<Self>>

Make a new DateTime from year, month, day, time components and current time zone. Read more
source§

fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self>

👎Deprecated since 0.4.23: use with_ymd_and_hms() instead
Makes a new Date from year, month, day and the current time zone. +This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. Read more
source§

fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>>

👎Deprecated since 0.4.23: use with_ymd_and_hms() instead
Makes a new Date from year, month, day and the current time zone. +This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. Read more
source§

fn yo(&self, year: i32, ordinal: u32) -> Date<Self>

👎Deprecated since 0.4.23: use from_local_datetime() with a NaiveDateTime instead
Makes a new Date from year, day of year (DOY or “ordinal”) and the current time zone. +This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. Read more
source§

fn yo_opt(&self, year: i32, ordinal: u32) -> LocalResult<Date<Self>>

👎Deprecated since 0.4.23: use from_local_datetime() with a NaiveDateTime instead
Makes a new Date from year, day of year (DOY or “ordinal”) and the current time zone. +This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. Read more
source§

fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self>

👎Deprecated since 0.4.23: use from_local_datetime() with a NaiveDateTime instead
Makes a new Date from ISO week date (year and week number), day of the week (DOW) and +the current time zone. +This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. +The resulting Date may have a different year from the input year. Read more
source§

fn isoywd_opt( + &self, + year: i32, + week: u32, + weekday: Weekday +) -> LocalResult<Date<Self>>

👎Deprecated since 0.4.23: use from_local_datetime() with a NaiveDateTime instead
Makes a new Date from ISO week date (year and week number), day of the week (DOW) and +the current time zone. +This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. +The resulting Date may have a different year from the input year. Read more
source§

fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self>

👎Deprecated since 0.4.23: use timestamp_opt() instead
Makes a new DateTime from the number of non-leap seconds +since January 1, 1970 0:00:00 UTC (aka “UNIX timestamp”) +and the number of nanoseconds since the last whole non-leap second. Read more
source§

fn timestamp_opt(&self, secs: i64, nsecs: u32) -> LocalResult<DateTime<Self>>

Makes a new DateTime from the number of non-leap seconds +since January 1, 1970 0:00:00 UTC (aka “UNIX timestamp”) +and the number of nanoseconds since the last whole non-leap second. Read more
source§

fn timestamp_millis(&self, millis: i64) -> DateTime<Self>

👎Deprecated since 0.4.23: use timestamp_millis_opt() instead
Makes a new DateTime from the number of non-leap milliseconds +since January 1, 1970 0:00:00 UTC (aka “UNIX timestamp”). Read more
source§

fn timestamp_millis_opt(&self, millis: i64) -> LocalResult<DateTime<Self>>

Makes a new DateTime from the number of non-leap milliseconds +since January 1, 1970 0:00:00 UTC (aka “UNIX timestamp”). Read more
source§

fn timestamp_nanos(&self, nanos: i64) -> DateTime<Self>

Makes a new DateTime from the number of non-leap nanoseconds +since January 1, 1970 0:00:00 UTC (aka “UNIX timestamp”). Read more
source§

fn timestamp_micros(&self, micros: i64) -> LocalResult<DateTime<Self>>

Makes a new DateTime from the number of non-leap microseconds +since January 1, 1970 0:00:00 UTC (aka “UNIX timestamp”). Read more
source§

fn datetime_from_str( + &self, + s: &str, + fmt: &str +) -> Result<DateTime<Self>, ParseError>

👎Deprecated since 0.4.29: use DateTime::parse_from_str instead
Parses a string with the specified format string and returns a +DateTime with the current offset. Read more
source§

fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>>

👎Deprecated since 0.4.23: use from_local_datetime() instead
Converts the local NaiveDate to the timezone-aware Date if possible.
source§

fn from_local_datetime( + &self, + local: &NaiveDateTime +) -> LocalResult<DateTime<Self>>

Converts the local NaiveDateTime to the timezone-aware DateTime if possible.
source§

fn from_utc_date(&self, utc: &NaiveDate) -> Date<Self>

👎Deprecated since 0.4.23: use from_utc_datetime() instead
Converts the UTC NaiveDate to the local time. +The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
source§

fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Self>

Converts the UTC NaiveDateTime to the local time. +The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
§

impl Copy for Tz

§

impl Eq for Tz

§

impl StructuralEq for Tz

§

impl StructuralPartialEq for Tz

Auto Trait Implementations§

§

impl RefUnwindSafe for Tz

§

impl Send for Tz

§

impl Sync for Tz

§

impl Unpin for Tz

§

impl UnwindSafe for Tz

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/model/fn.get_milliseconds_since_epoch.html b/ironcalc_base/model/fn.get_milliseconds_since_epoch.html new file mode 100644 index 0000000..eaf035e --- /dev/null +++ b/ironcalc_base/model/fn.get_milliseconds_since_epoch.html @@ -0,0 +1 @@ +get_milliseconds_since_epoch in ironcalc_base::model - Rust
pub fn get_milliseconds_since_epoch() -> i64
\ No newline at end of file diff --git a/ironcalc_base/model/index.html b/ironcalc_base/model/index.html new file mode 100644 index 0000000..cac96ac --- /dev/null +++ b/ironcalc_base/model/index.html @@ -0,0 +1,6 @@ +ironcalc_base::model - Rust

Module ironcalc_base::model

source ·

Structs

  • A model includes: +* A Workbook: An internal representation of and Excel workbook +* Parsed Formulas: All the formulas in the workbook are parsed here (runtime only) +* A list of cells with its status (evaluating, evaluated, not evaluated) +* A dictionary with the shared strings and their indices. +This is an optimization for large files (~1 million rows)

Enums

Functions

\ No newline at end of file diff --git a/ironcalc_base/model/sidebar-items.js b/ironcalc_base/model/sidebar-items.js new file mode 100644 index 0000000..282f544 --- /dev/null +++ b/ironcalc_base/model/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["CellState","ParsedDefinedName","Tz"],"fn":["get_milliseconds_since_epoch"],"struct":["CellIndex","Model","Style"]}; \ No newline at end of file diff --git a/ironcalc_base/model/struct.CellIndex.html b/ironcalc_base/model/struct.CellIndex.html new file mode 100644 index 0000000..155e15e --- /dev/null +++ b/ironcalc_base/model/struct.CellIndex.html @@ -0,0 +1,16 @@ +CellIndex in ironcalc_base::model - Rust
pub struct CellIndex {
+    pub index: u32,
+    pub row: i32,
+    pub column: i32,
+}

Fields§

§index: u32§row: i32§column: i32

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/model/struct.Model.html b/ironcalc_base/model/struct.Model.html new file mode 100644 index 0000000..a06fc00 --- /dev/null +++ b/ironcalc_base/model/struct.Model.html @@ -0,0 +1,308 @@ +Model in ironcalc_base::model - Rust

Struct ironcalc_base::model::Model

source ·
pub struct Model {
+    pub workbook: Workbook,
+    pub parsed_formulas: Vec<Vec<Node>>,
+    pub parsed_defined_names: HashMap<(Option<u32>, String), ParsedDefinedName>,
+    pub shared_strings: HashMap<String, usize>,
+    pub parser: Parser,
+    pub cells: HashMap<(u32, i32, i32), CellState>,
+    pub locale: Locale,
+    pub language: Language,
+    pub tz: Tz,
+}
Expand description

A model includes: +* A Workbook: An internal representation of and Excel workbook +* Parsed Formulas: All the formulas in the workbook are parsed here (runtime only) +* A list of cells with its status (evaluating, evaluated, not evaluated) +* A dictionary with the shared strings and their indices. +This is an optimization for large files (~1 million rows)

+

Fields§

§workbook: Workbook§parsed_formulas: Vec<Vec<Node>>§parsed_defined_names: HashMap<(Option<u32>, String), ParsedDefinedName>§shared_strings: HashMap<String, usize>§parser: Parser§cells: HashMap<(u32, i32, i32), CellState>§locale: Locale§language: Language§tz: Tz

Implementations§

source§

impl Model

source

pub fn set_sheet_color(&mut self, sheet: u32, color: &str) -> Result<(), String>

source

pub fn is_empty_cell( + &self, + sheet: u32, + row: i32, + column: i32 +) -> Result<bool, String>

Returns true if cell is completely empty. +Cell with formula that evaluates to empty string is not considered empty.

+
source

pub fn from_json(s: &str) -> Result<Model, String>

Returns a model from a String representation of a workbook

+
source

pub fn from_workbook(workbook: Workbook) -> Result<Model, String>

source

pub fn parse_reference(&self, s: &str) -> Option<CellReference>

Parses a reference like “Sheet1!B4” into {0, 2, 4}

+
source

pub fn move_cell_value_to_area( + &mut self, + value: &str, + source: &CellReferenceIndex, + target: &CellReferenceIndex, + area: &Area +) -> Result<String, String>

moves the value in area from source to target.

+
source

pub fn extend_to( + &self, + sheet: u32, + row: i32, + column: i32, + target_row: i32, + target_column: i32 +) -> Result<String, String>

‘Extends’ the value from cell [sheet, row, column] to [target_row, target_column]

+
source

pub fn extend_copied_value( + &mut self, + value: &str, + source_sheet_name: &str, + source: &CellReferenceIndex, + target: &CellReferenceIndex +) -> Result<String, String>

‘Extends’ value from cell [sheet, row, column] to [target_row, target_column]

+
source

pub fn cell_formula( + &self, + sheet: u32, + row: i32, + column: i32 +) -> Result<Option<String>, String>

source

pub fn update_cell_with_text( + &mut self, + sheet: u32, + row: i32, + column: i32, + value: &str +)

Updates the value of a cell with some text +It does not change the style unless needs to add “quoting”

+
source

pub fn update_cell_with_bool( + &mut self, + sheet: u32, + row: i32, + column: i32, + value: bool +)

Updates the value of a cell with a boolean value +It does not change the style

+
source

pub fn update_cell_with_number( + &mut self, + sheet: u32, + row: i32, + column: i32, + value: f64 +)

Updates the value of a cell with a number +It does not change the style

+
source

pub fn update_cell_with_formula( + &mut self, + sheet: u32, + row: i32, + column: i32, + formula: String +) -> Result<(), String>

Updates the formula of given cell +It does not change the style unless needs to add “quoting” +Expects the formula to start with “=”

+
source

pub fn set_user_input( + &mut self, + sheet: u32, + row: i32, + column: i32, + value: String +)

Sets a cell parametrized by (sheet, row, column) with value +This mimics a user entering a value on a cell. +If you enter a currency $100 it will set as a number and update the style +Note that for currencies/percentage there is only one possible style +The value is always a string, so we need to try to cast it into numbers/booleans/errors

+
source

pub fn get_cell_value_by_ref(&self, cell_ref: &str) -> Result<CellValue, String>

Gets the Excel Value (Bool, Number, String) of a cell

+
source

pub fn get_cell_value_by_index( + &self, + sheet_index: u32, + row: i32, + column: i32 +) -> Result<CellValue, String>

source

pub fn formatted_cell_value( + &self, + sheet_index: u32, + row: i32, + column: i32 +) -> Result<String, String>

source

pub fn get_cell_content( + &self, + sheet: u32, + row: i32, + column: i32 +) -> Result<String, String>

Returns a string with the cell content. If there is a formula returns the formula +If the cell is empty returns the empty string +Raises an error if there is no worksheet

+
source

pub fn get_all_cells(&self) -> Vec<CellIndex>

Returns a list of all cells

+
source

pub fn evaluate(&mut self)

Evaluates the model with a top-down recursive algorithm

+
source

pub fn set_cell_empty( + &mut self, + sheet: u32, + row: i32, + column: i32 +) -> Result<(), String>

Sets cell to empty. Can be used to delete value without affecting style.

+
source

pub fn delete_cell( + &mut self, + sheet: u32, + row: i32, + column: i32 +) -> Result<(), String>

Deletes a cell by removing it from worksheet data.

+
source

pub fn get_cell_style_index(&self, sheet: u32, row: i32, column: i32) -> i32

source

pub fn get_style_for_cell(&self, sheet: u32, row: i32, column: i32) -> Style

source

pub fn to_json_str(&self) -> String

Returns a JSON string of the workbook

+
source

pub fn sheet_markup(&self, sheet: u32) -> Result<String, String>

Returns markup representation of the given sheet.

+
source

pub fn set_currency(&mut self, iso: &str) -> Result<(), &str>

source

pub fn get_frozen_rows(&self, sheet: u32) -> Result<i32, String>

source

pub fn get_frozen_columns(&self, sheet: u32) -> Result<i32, String>

source

pub fn set_frozen_rows( + &mut self, + sheet: u32, + frozen_rows: i32 +) -> Result<(), String>

source

pub fn set_frozen_columns( + &mut self, + sheet: u32, + frozen_columns: i32 +) -> Result<(), String>

source§

impl Model

source

pub fn get_new_sheet_id(&self) -> u32

source

pub fn new_sheet(&mut self)

Adds a sheet with a automatically generated name

+
source

pub fn insert_sheet( + &mut self, + sheet_name: &str, + sheet_index: u32, + sheet_id: Option<u32> +) -> Result<(), String>

Inserts a sheet with a particular index +Fails if a worksheet with that name already exists or the name is invalid +Fails if the index is too large

+
source

pub fn add_sheet(&mut self, sheet_name: &str) -> Result<(), String>

Adds a sheet with a specific name +Fails if a worksheet with that name already exists or the name is invalid

+
source

pub fn rename_sheet( + &mut self, + old_name: &str, + new_name: &str +) -> Result<(), String>

Renames a sheet and updates all existing references to that sheet. +It can fail if:

+
    +
  • The original sheet does not exists
  • +
  • The target sheet already exists
  • +
  • The target sheet name is invalid
  • +
+
source

pub fn rename_sheet_by_index( + &mut self, + sheet_index: u32, + new_name: &str +) -> Result<(), String>

Renames a sheet and updates all existing references to that sheet. +It can fail if:

+
    +
  • The original index is too large
  • +
  • The target sheet name already exists
  • +
  • The target sheet name is invalid
  • +
+
source

pub fn delete_sheet(&mut self, sheet_index: u32) -> Result<(), String>

Deletes a sheet by index. Fails if:

+
    +
  • The sheet does not exists
  • +
  • It is the last sheet
  • +
+
source

pub fn delete_sheet_by_name(&mut self, name: &str) -> Result<(), String>

Deletes a sheet by name. Fails if:

+
    +
  • The sheet does not exists
  • +
  • It is the last sheet
  • +
+
source

pub fn delete_sheet_by_sheet_id(&mut self, sheet_id: u32) -> Result<(), String>

Deletes a sheet by sheet_id. Fails if:

+
    +
  • The sheet by sheet_id does not exists
  • +
  • It is the last sheet
  • +
+
source

pub fn new_empty( + name: &str, + locale_id: &str, + timezone: &str +) -> Result<Model, String>

Creates a new workbook with one empty sheet

+
source§

impl Model

source

pub fn insert_columns( + &mut self, + sheet: u32, + column: i32, + column_count: i32 +) -> Result<(), String>

Inserts one or more new columns into the model at the specified index.

+

This method shifts existing columns to the right to make space for the new columns.

+
Arguments
+
    +
  • sheet - The sheet number to retrieve columns from.
  • +
  • column - The index at which the new columns should be inserted.
  • +
  • column_count - The number of columns to insert.
  • +
+
source

pub fn delete_columns( + &mut self, + sheet: u32, + column: i32, + column_count: i32 +) -> Result<(), String>

Deletes one or more columns from the model starting at the specified index.

+
Arguments
+
    +
  • sheet - The sheet number to retrieve columns from.
  • +
  • column - The index of the first column to delete.
  • +
  • count - The number of columns to delete.
  • +
+
source

pub fn insert_rows( + &mut self, + sheet: u32, + row: i32, + row_count: i32 +) -> Result<(), String>

Inserts one or more new rows into the model at the specified index.

+
Arguments
+
    +
  • sheet - The sheet number to retrieve columns from.
  • +
  • row - The index at which the new rows should be inserted.
  • +
  • row_count - The number of rows to insert.
  • +
+
source

pub fn delete_rows( + &mut self, + sheet: u32, + row: i32, + row_count: i32 +) -> Result<(), String>

Deletes one or more rows from the model starting at the specified index.

+
Arguments
+
    +
  • sheet - The sheet number to retrieve columns from.
  • +
  • row - The index of the first row to delete.
  • +
  • row_count - The number of rows to delete.
  • +
+
source

pub fn move_column_action( + &mut self, + sheet: u32, + column: i32, + delta: i32 +) -> Result<(), &'static str>

Displaces cells due to a move column action +from initial_column to target_column = initial_column + column_delta +References will be updated following: +Cell references:

+
    +
  • All cell references to initial_column will go to target_column
  • +
  • All cell references to columns in between (initial_column, target_column] will be displaced one to the left
  • +
  • All other cell references are left unchanged +Ranges. This is the tricky bit:
  • +
  • Column is one of the extremes of the range. The new extreme would be target_column. +Range is then normalized
  • +
  • Any other case, range is left unchanged. +NOTE: This does NOT move the data in the columns or move the colum styles
  • +
+
source§

impl Model

source

pub fn set_cell_style( + &mut self, + sheet: u32, + row: i32, + column: i32, + style: &Style +) -> Result<(), String>

source

pub fn copy_cell_style( + &mut self, + source_cell: (u32, i32, i32), + destination_cell: (u32, i32, i32) +) -> Result<(), String>

source

pub fn set_cell_style_by_name( + &mut self, + sheet: u32, + row: i32, + column: i32, + style_name: &str +) -> Result<(), String>

Sets the style “style_name” in cell

+
source

pub fn set_sheet_style( + &mut self, + sheet: u32, + style_name: &str +) -> Result<(), String>

source

pub fn set_sheet_row_style( + &mut self, + sheet: u32, + row: i32, + style_name: &str +) -> Result<(), String>

source

pub fn set_sheet_column_style( + &mut self, + sheet: u32, + column: i32, + style_name: &str +) -> Result<(), String>

source§

impl Model

source

pub fn forward_references( + &mut self, + source_area: &Area, + target: &CellReferenceIndex +) -> Result<Vec<SetCellValue>, String>

Trait Implementations§

source§

impl Clone for Model

source§

fn clone(&self) -> Model

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

§

impl RefUnwindSafe for Model

§

impl Send for Model

§

impl Sync for Model

§

impl Unpin for Model

§

impl UnwindSafe for Model

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/model/struct.Style.html b/ironcalc_base/model/struct.Style.html new file mode 100644 index 0000000..ea3436b --- /dev/null +++ b/ironcalc_base/model/struct.Style.html @@ -0,0 +1,25 @@ +Style in ironcalc_base::model - Rust

Struct ironcalc_base::model::Style

source ·
pub struct Style {
+    pub alignment: Option<Alignment>,
+    pub num_fmt: String,
+    pub fill: Fill,
+    pub font: Font,
+    pub border: Border,
+    pub quote_prefix: bool,
+}

Fields§

§alignment: Option<Alignment>§num_fmt: String§fill: Fill§font: Font§border: Border§quote_prefix: bool

Trait Implementations§

source§

impl Clone for Style

source§

fn clone(&self) -> Style

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Style

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Style

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Style

source§

fn eq(&self, other: &Style) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Style

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for Style

source§

impl StructuralEq for Style

source§

impl StructuralPartialEq for Style

Auto Trait Implementations§

§

impl RefUnwindSafe for Style

§

impl Send for Style

§

impl Sync for Style

§

impl Unpin for Style

§

impl UnwindSafe for Style

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/new_empty/constant.APPLICATION.html b/ironcalc_base/new_empty/constant.APPLICATION.html new file mode 100644 index 0000000..5142cae --- /dev/null +++ b/ironcalc_base/new_empty/constant.APPLICATION.html @@ -0,0 +1 @@ +APPLICATION in ironcalc_base::new_empty - Rust
pub const APPLICATION: &str = "IronCalc Sheets";
\ No newline at end of file diff --git a/ironcalc_base/new_empty/constant.APP_VERSION.html b/ironcalc_base/new_empty/constant.APP_VERSION.html new file mode 100644 index 0000000..b573bb0 --- /dev/null +++ b/ironcalc_base/new_empty/constant.APP_VERSION.html @@ -0,0 +1 @@ +APP_VERSION in ironcalc_base::new_empty - Rust
pub const APP_VERSION: &str = "10.0000";
\ No newline at end of file diff --git a/ironcalc_base/new_empty/constant.IRONCALC_USER.html b/ironcalc_base/new_empty/constant.IRONCALC_USER.html new file mode 100644 index 0000000..bf119d2 --- /dev/null +++ b/ironcalc_base/new_empty/constant.IRONCALC_USER.html @@ -0,0 +1 @@ +IRONCALC_USER in ironcalc_base::new_empty - Rust
pub const IRONCALC_USER: &str = "IronCalc User";
\ No newline at end of file diff --git a/ironcalc_base/new_empty/enum.Tz.html b/ironcalc_base/new_empty/enum.Tz.html new file mode 100644 index 0000000..622f99a --- /dev/null +++ b/ironcalc_base/new_empty/enum.Tz.html @@ -0,0 +1,1266 @@ +Tz in ironcalc_base::new_empty - Rust
pub enum Tz {
+
Show 595 variants Africa__Abidjan, + Africa__Accra, + Africa__Addis_Ababa, + Africa__Algiers, + Africa__Asmara, + Africa__Asmera, + Africa__Bamako, + Africa__Bangui, + Africa__Banjul, + Africa__Bissau, + Africa__Blantyre, + Africa__Brazzaville, + Africa__Bujumbura, + Africa__Cairo, + Africa__Casablanca, + Africa__Ceuta, + Africa__Conakry, + Africa__Dakar, + Africa__Dar_es_Salaam, + Africa__Djibouti, + Africa__Douala, + Africa__El_Aaiun, + Africa__Freetown, + Africa__Gaborone, + Africa__Harare, + Africa__Johannesburg, + Africa__Juba, + Africa__Kampala, + Africa__Khartoum, + Africa__Kigali, + Africa__Kinshasa, + Africa__Lagos, + Africa__Libreville, + Africa__Lome, + Africa__Luanda, + Africa__Lubumbashi, + Africa__Lusaka, + Africa__Malabo, + Africa__Maputo, + Africa__Maseru, + Africa__Mbabane, + Africa__Mogadishu, + Africa__Monrovia, + Africa__Nairobi, + Africa__Ndjamena, + Africa__Niamey, + Africa__Nouakchott, + Africa__Ouagadougou, + Africa__PortoNovo, + Africa__Sao_Tome, + Africa__Timbuktu, + Africa__Tripoli, + Africa__Tunis, + Africa__Windhoek, + America__Adak, + America__Anchorage, + America__Anguilla, + America__Antigua, + America__Araguaina, + America__Argentina__Buenos_Aires, + America__Argentina__Catamarca, + America__Argentina__ComodRivadavia, + America__Argentina__Cordoba, + America__Argentina__Jujuy, + America__Argentina__La_Rioja, + America__Argentina__Mendoza, + America__Argentina__Rio_Gallegos, + America__Argentina__Salta, + America__Argentina__San_Juan, + America__Argentina__San_Luis, + America__Argentina__Tucuman, + America__Argentina__Ushuaia, + America__Aruba, + America__Asuncion, + America__Atikokan, + America__Atka, + America__Bahia, + America__Bahia_Banderas, + America__Barbados, + America__Belem, + America__Belize, + America__BlancSablon, + America__Boa_Vista, + America__Bogota, + America__Boise, + America__Buenos_Aires, + America__Cambridge_Bay, + America__Campo_Grande, + America__Cancun, + America__Caracas, + America__Catamarca, + America__Cayenne, + America__Cayman, + America__Chicago, + America__Chihuahua, + America__Coral_Harbour, + America__Cordoba, + America__Costa_Rica, + America__Creston, + America__Cuiaba, + America__Curacao, + America__Danmarkshavn, + America__Dawson, + America__Dawson_Creek, + America__Denver, + America__Detroit, + America__Dominica, + America__Edmonton, + America__Eirunepe, + America__El_Salvador, + America__Ensenada, + America__Fort_Nelson, + America__Fort_Wayne, + America__Fortaleza, + America__Glace_Bay, + America__Godthab, + America__Goose_Bay, + America__Grand_Turk, + America__Grenada, + America__Guadeloupe, + America__Guatemala, + America__Guayaquil, + America__Guyana, + America__Halifax, + America__Havana, + America__Hermosillo, + America__Indiana__Indianapolis, + America__Indiana__Knox, + America__Indiana__Marengo, + America__Indiana__Petersburg, + America__Indiana__Tell_City, + America__Indiana__Vevay, + America__Indiana__Vincennes, + America__Indiana__Winamac, + America__Indianapolis, + America__Inuvik, + America__Iqaluit, + America__Jamaica, + America__Jujuy, + America__Juneau, + America__Kentucky__Louisville, + America__Kentucky__Monticello, + America__Knox_IN, + America__Kralendijk, + America__La_Paz, + America__Lima, + America__Los_Angeles, + America__Louisville, + America__Lower_Princes, + America__Maceio, + America__Managua, + America__Manaus, + America__Marigot, + America__Martinique, + America__Matamoros, + America__Mazatlan, + America__Mendoza, + America__Menominee, + America__Merida, + America__Metlakatla, + America__Mexico_City, + America__Miquelon, + America__Moncton, + America__Monterrey, + America__Montevideo, + America__Montreal, + America__Montserrat, + America__Nassau, + America__New_York, + America__Nipigon, + America__Nome, + America__Noronha, + America__North_Dakota__Beulah, + America__North_Dakota__Center, + America__North_Dakota__New_Salem, + America__Nuuk, + America__Ojinaga, + America__Panama, + America__Pangnirtung, + America__Paramaribo, + America__Phoenix, + America__PortauPrince, + America__Port_of_Spain, + America__Porto_Acre, + America__Porto_Velho, + America__Puerto_Rico, + America__Punta_Arenas, + America__Rainy_River, + America__Rankin_Inlet, + America__Recife, + America__Regina, + America__Resolute, + America__Rio_Branco, + America__Rosario, + America__Santa_Isabel, + America__Santarem, + America__Santiago, + America__Santo_Domingo, + America__Sao_Paulo, + America__Scoresbysund, + America__Shiprock, + America__Sitka, + America__St_Barthelemy, + America__St_Johns, + America__St_Kitts, + America__St_Lucia, + America__St_Thomas, + America__St_Vincent, + America__Swift_Current, + America__Tegucigalpa, + America__Thule, + America__Thunder_Bay, + America__Tijuana, + America__Toronto, + America__Tortola, + America__Vancouver, + America__Virgin, + America__Whitehorse, + America__Winnipeg, + America__Yakutat, + America__Yellowknife, + Antarctica__Casey, + Antarctica__Davis, + Antarctica__DumontDUrville, + Antarctica__Macquarie, + Antarctica__Mawson, + Antarctica__McMurdo, + Antarctica__Palmer, + Antarctica__Rothera, + Antarctica__South_Pole, + Antarctica__Syowa, + Antarctica__Troll, + Antarctica__Vostok, + Arctic__Longyearbyen, + Asia__Aden, + Asia__Almaty, + Asia__Amman, + Asia__Anadyr, + Asia__Aqtau, + Asia__Aqtobe, + Asia__Ashgabat, + Asia__Ashkhabad, + Asia__Atyrau, + Asia__Baghdad, + Asia__Bahrain, + Asia__Baku, + Asia__Bangkok, + Asia__Barnaul, + Asia__Beirut, + Asia__Bishkek, + Asia__Brunei, + Asia__Calcutta, + Asia__Chita, + Asia__Choibalsan, + Asia__Chongqing, + Asia__Chungking, + Asia__Colombo, + Asia__Dacca, + Asia__Damascus, + Asia__Dhaka, + Asia__Dili, + Asia__Dubai, + Asia__Dushanbe, + Asia__Famagusta, + Asia__Gaza, + Asia__Harbin, + Asia__Hebron, + Asia__Ho_Chi_Minh, + Asia__Hong_Kong, + Asia__Hovd, + Asia__Irkutsk, + Asia__Istanbul, + Asia__Jakarta, + Asia__Jayapura, + Asia__Jerusalem, + Asia__Kabul, + Asia__Kamchatka, + Asia__Karachi, + Asia__Kashgar, + Asia__Kathmandu, + Asia__Katmandu, + Asia__Khandyga, + Asia__Kolkata, + Asia__Krasnoyarsk, + Asia__Kuala_Lumpur, + Asia__Kuching, + Asia__Kuwait, + Asia__Macao, + Asia__Macau, + Asia__Magadan, + Asia__Makassar, + Asia__Manila, + Asia__Muscat, + Asia__Nicosia, + Asia__Novokuznetsk, + Asia__Novosibirsk, + Asia__Omsk, + Asia__Oral, + Asia__Phnom_Penh, + Asia__Pontianak, + Asia__Pyongyang, + Asia__Qatar, + Asia__Qostanay, + Asia__Qyzylorda, + Asia__Rangoon, + Asia__Riyadh, + Asia__Saigon, + Asia__Sakhalin, + Asia__Samarkand, + Asia__Seoul, + Asia__Shanghai, + Asia__Singapore, + Asia__Srednekolymsk, + Asia__Taipei, + Asia__Tashkent, + Asia__Tbilisi, + Asia__Tehran, + Asia__Tel_Aviv, + Asia__Thimbu, + Asia__Thimphu, + Asia__Tokyo, + Asia__Tomsk, + Asia__Ujung_Pandang, + Asia__Ulaanbaatar, + Asia__Ulan_Bator, + Asia__Urumqi, + Asia__UstNera, + Asia__Vientiane, + Asia__Vladivostok, + Asia__Yakutsk, + Asia__Yangon, + Asia__Yekaterinburg, + Asia__Yerevan, + Atlantic__Azores, + Atlantic__Bermuda, + Atlantic__Canary, + Atlantic__Cape_Verde, + Atlantic__Faeroe, + Atlantic__Faroe, + Atlantic__Jan_Mayen, + Atlantic__Madeira, + Atlantic__Reykjavik, + Atlantic__South_Georgia, + Atlantic__St_Helena, + Atlantic__Stanley, + Australia__ACT, + Australia__Adelaide, + Australia__Brisbane, + Australia__Broken_Hill, + Australia__Canberra, + Australia__Currie, + Australia__Darwin, + Australia__Eucla, + Australia__Hobart, + Australia__LHI, + Australia__Lindeman, + Australia__Lord_Howe, + Australia__Melbourne, + Australia__NSW, + Australia__North, + Australia__Perth, + Australia__Queensland, + Australia__South, + Australia__Sydney, + Australia__Tasmania, + Australia__Victoria, + Australia__West, + Australia__Yancowinna, + Brazil__Acre, + Brazil__DeNoronha, + Brazil__East, + Brazil__West, + CET, + CST6CDT, + Canada__Atlantic, + Canada__Central, + Canada__Eastern, + Canada__Mountain, + Canada__Newfoundland, + Canada__Pacific, + Canada__Saskatchewan, + Canada__Yukon, + Chile__Continental, + Chile__EasterIsland, + Cuba, + EET, + EST, + EST5EDT, + Egypt, + Eire, + Etc__GMT, + Etc__GMTPlus0, + Etc__GMTPlus1, + Etc__GMTPlus10, + Etc__GMTPlus11, + Etc__GMTPlus12, + Etc__GMTPlus2, + Etc__GMTPlus3, + Etc__GMTPlus4, + Etc__GMTPlus5, + Etc__GMTPlus6, + Etc__GMTPlus7, + Etc__GMTPlus8, + Etc__GMTPlus9, + Etc__GMTMinus0, + Etc__GMTMinus1, + Etc__GMTMinus10, + Etc__GMTMinus11, + Etc__GMTMinus12, + Etc__GMTMinus13, + Etc__GMTMinus14, + Etc__GMTMinus2, + Etc__GMTMinus3, + Etc__GMTMinus4, + Etc__GMTMinus5, + Etc__GMTMinus6, + Etc__GMTMinus7, + Etc__GMTMinus8, + Etc__GMTMinus9, + Etc__GMT0, + Etc__Greenwich, + Etc__UCT, + Etc__UTC, + Etc__Universal, + Etc__Zulu, + Europe__Amsterdam, + Europe__Andorra, + Europe__Astrakhan, + Europe__Athens, + Europe__Belfast, + Europe__Belgrade, + Europe__Berlin, + Europe__Bratislava, + Europe__Brussels, + Europe__Bucharest, + Europe__Budapest, + Europe__Busingen, + Europe__Chisinau, + Europe__Copenhagen, + Europe__Dublin, + Europe__Gibraltar, + Europe__Guernsey, + Europe__Helsinki, + Europe__Isle_of_Man, + Europe__Istanbul, + Europe__Jersey, + Europe__Kaliningrad, + Europe__Kiev, + Europe__Kirov, + Europe__Kyiv, + Europe__Lisbon, + Europe__Ljubljana, + Europe__London, + Europe__Luxembourg, + Europe__Madrid, + Europe__Malta, + Europe__Mariehamn, + Europe__Minsk, + Europe__Monaco, + Europe__Moscow, + Europe__Nicosia, + Europe__Oslo, + Europe__Paris, + Europe__Podgorica, + Europe__Prague, + Europe__Riga, + Europe__Rome, + Europe__Samara, + Europe__San_Marino, + Europe__Sarajevo, + Europe__Saratov, + Europe__Simferopol, + Europe__Skopje, + Europe__Sofia, + Europe__Stockholm, + Europe__Tallinn, + Europe__Tirane, + Europe__Tiraspol, + Europe__Ulyanovsk, + Europe__Uzhgorod, + Europe__Vaduz, + Europe__Vatican, + Europe__Vienna, + Europe__Vilnius, + Europe__Volgograd, + Europe__Warsaw, + Europe__Zagreb, + Europe__Zaporozhye, + Europe__Zurich, + GB, + GBEire, + GMT, + GMTPlus0, + GMTMinus0, + GMT0, + Greenwich, + HST, + Hongkong, + Iceland, + Indian__Antananarivo, + Indian__Chagos, + Indian__Christmas, + Indian__Cocos, + Indian__Comoro, + Indian__Kerguelen, + Indian__Mahe, + Indian__Maldives, + Indian__Mauritius, + Indian__Mayotte, + Indian__Reunion, + Iran, + Israel, + Jamaica, + Japan, + Kwajalein, + Libya, + MET, + MST, + MST7MDT, + Mexico__BajaNorte, + Mexico__BajaSur, + Mexico__General, + NZ, + NZCHAT, + Navajo, + PRC, + PST8PDT, + Pacific__Apia, + Pacific__Auckland, + Pacific__Bougainville, + Pacific__Chatham, + Pacific__Chuuk, + Pacific__Easter, + Pacific__Efate, + Pacific__Enderbury, + Pacific__Fakaofo, + Pacific__Fiji, + Pacific__Funafuti, + Pacific__Galapagos, + Pacific__Gambier, + Pacific__Guadalcanal, + Pacific__Guam, + Pacific__Honolulu, + Pacific__Johnston, + Pacific__Kanton, + Pacific__Kiritimati, + Pacific__Kosrae, + Pacific__Kwajalein, + Pacific__Majuro, + Pacific__Marquesas, + Pacific__Midway, + Pacific__Nauru, + Pacific__Niue, + Pacific__Norfolk, + Pacific__Noumea, + Pacific__Pago_Pago, + Pacific__Palau, + Pacific__Pitcairn, + Pacific__Pohnpei, + Pacific__Ponape, + Pacific__Port_Moresby, + Pacific__Rarotonga, + Pacific__Saipan, + Pacific__Samoa, + Pacific__Tahiti, + Pacific__Tarawa, + Pacific__Tongatapu, + Pacific__Truk, + Pacific__Wake, + Pacific__Wallis, + Pacific__Yap, + Poland, + Portugal, + ROC, + ROK, + Singapore, + Turkey, + UCT, + US__Alaska, + US__Aleutian, + US__Arizona, + US__Central, + US__EastIndiana, + US__Eastern, + US__Hawaii, + US__IndianaStarke, + US__Michigan, + US__Mountain, + US__Pacific, + US__Samoa, + UTC, + Universal, + WSU, + WET, + Zulu, +
}
Expand description

TimeZones built at compile time from the tz database

+

This implements chrono::TimeZone so that it may be used in and to +construct chrono’s DateTime type. See the root module documentation +for details.

+

Variants§

§

Africa__Abidjan

Africa/Abidjan

+
§

Africa__Accra

Africa/Accra

+
§

Africa__Addis_Ababa

Africa/Addis_Ababa

+
§

Africa__Algiers

Africa/Algiers

+
§

Africa__Asmara

Africa/Asmara

+
§

Africa__Asmera

Africa/Asmera

+
§

Africa__Bamako

Africa/Bamako

+
§

Africa__Bangui

Africa/Bangui

+
§

Africa__Banjul

Africa/Banjul

+
§

Africa__Bissau

Africa/Bissau

+
§

Africa__Blantyre

Africa/Blantyre

+
§

Africa__Brazzaville

Africa/Brazzaville

+
§

Africa__Bujumbura

Africa/Bujumbura

+
§

Africa__Cairo

Africa/Cairo

+
§

Africa__Casablanca

Africa/Casablanca

+
§

Africa__Ceuta

Africa/Ceuta

+
§

Africa__Conakry

Africa/Conakry

+
§

Africa__Dakar

Africa/Dakar

+
§

Africa__Dar_es_Salaam

Africa/Dar_es_Salaam

+
§

Africa__Djibouti

Africa/Djibouti

+
§

Africa__Douala

Africa/Douala

+
§

Africa__El_Aaiun

Africa/El_Aaiun

+
§

Africa__Freetown

Africa/Freetown

+
§

Africa__Gaborone

Africa/Gaborone

+
§

Africa__Harare

Africa/Harare

+
§

Africa__Johannesburg

Africa/Johannesburg

+
§

Africa__Juba

Africa/Juba

+
§

Africa__Kampala

Africa/Kampala

+
§

Africa__Khartoum

Africa/Khartoum

+
§

Africa__Kigali

Africa/Kigali

+
§

Africa__Kinshasa

Africa/Kinshasa

+
§

Africa__Lagos

Africa/Lagos

+
§

Africa__Libreville

Africa/Libreville

+
§

Africa__Lome

Africa/Lome

+
§

Africa__Luanda

Africa/Luanda

+
§

Africa__Lubumbashi

Africa/Lubumbashi

+
§

Africa__Lusaka

Africa/Lusaka

+
§

Africa__Malabo

Africa/Malabo

+
§

Africa__Maputo

Africa/Maputo

+
§

Africa__Maseru

Africa/Maseru

+
§

Africa__Mbabane

Africa/Mbabane

+
§

Africa__Mogadishu

Africa/Mogadishu

+
§

Africa__Monrovia

Africa/Monrovia

+
§

Africa__Nairobi

Africa/Nairobi

+
§

Africa__Ndjamena

Africa/Ndjamena

+
§

Africa__Niamey

Africa/Niamey

+
§

Africa__Nouakchott

Africa/Nouakchott

+
§

Africa__Ouagadougou

Africa/Ouagadougou

+
§

Africa__PortoNovo

Africa/Porto-Novo

+
§

Africa__Sao_Tome

Africa/Sao_Tome

+
§

Africa__Timbuktu

Africa/Timbuktu

+
§

Africa__Tripoli

Africa/Tripoli

+
§

Africa__Tunis

Africa/Tunis

+
§

Africa__Windhoek

Africa/Windhoek

+
§

America__Adak

America/Adak

+
§

America__Anchorage

America/Anchorage

+
§

America__Anguilla

America/Anguilla

+
§

America__Antigua

America/Antigua

+
§

America__Araguaina

America/Araguaina

+
§

America__Argentina__Buenos_Aires

America/Argentina/Buenos_Aires

+
§

America__Argentina__Catamarca

America/Argentina/Catamarca

+
§

America__Argentina__ComodRivadavia

America/Argentina/ComodRivadavia

+
§

America__Argentina__Cordoba

America/Argentina/Cordoba

+
§

America__Argentina__Jujuy

America/Argentina/Jujuy

+
§

America__Argentina__La_Rioja

America/Argentina/La_Rioja

+
§

America__Argentina__Mendoza

America/Argentina/Mendoza

+
§

America__Argentina__Rio_Gallegos

America/Argentina/Rio_Gallegos

+
§

America__Argentina__Salta

America/Argentina/Salta

+
§

America__Argentina__San_Juan

America/Argentina/San_Juan

+
§

America__Argentina__San_Luis

America/Argentina/San_Luis

+
§

America__Argentina__Tucuman

America/Argentina/Tucuman

+
§

America__Argentina__Ushuaia

America/Argentina/Ushuaia

+
§

America__Aruba

America/Aruba

+
§

America__Asuncion

America/Asuncion

+
§

America__Atikokan

America/Atikokan

+
§

America__Atka

America/Atka

+
§

America__Bahia

America/Bahia

+
§

America__Bahia_Banderas

America/Bahia_Banderas

+
§

America__Barbados

America/Barbados

+
§

America__Belem

America/Belem

+
§

America__Belize

America/Belize

+
§

America__BlancSablon

America/Blanc-Sablon

+
§

America__Boa_Vista

America/Boa_Vista

+
§

America__Bogota

America/Bogota

+
§

America__Boise

America/Boise

+
§

America__Buenos_Aires

America/Buenos_Aires

+
§

America__Cambridge_Bay

America/Cambridge_Bay

+
§

America__Campo_Grande

America/Campo_Grande

+
§

America__Cancun

America/Cancun

+
§

America__Caracas

America/Caracas

+
§

America__Catamarca

America/Catamarca

+
§

America__Cayenne

America/Cayenne

+
§

America__Cayman

America/Cayman

+
§

America__Chicago

America/Chicago

+
§

America__Chihuahua

America/Chihuahua

+
§

America__Coral_Harbour

America/Coral_Harbour

+
§

America__Cordoba

America/Cordoba

+
§

America__Costa_Rica

America/Costa_Rica

+
§

America__Creston

America/Creston

+
§

America__Cuiaba

America/Cuiaba

+
§

America__Curacao

America/Curacao

+
§

America__Danmarkshavn

America/Danmarkshavn

+
§

America__Dawson

America/Dawson

+
§

America__Dawson_Creek

America/Dawson_Creek

+
§

America__Denver

America/Denver

+
§

America__Detroit

America/Detroit

+
§

America__Dominica

America/Dominica

+
§

America__Edmonton

America/Edmonton

+
§

America__Eirunepe

America/Eirunepe

+
§

America__El_Salvador

America/El_Salvador

+
§

America__Ensenada

America/Ensenada

+
§

America__Fort_Nelson

America/Fort_Nelson

+
§

America__Fort_Wayne

America/Fort_Wayne

+
§

America__Fortaleza

America/Fortaleza

+
§

America__Glace_Bay

America/Glace_Bay

+
§

America__Godthab

America/Godthab

+
§

America__Goose_Bay

America/Goose_Bay

+
§

America__Grand_Turk

America/Grand_Turk

+
§

America__Grenada

America/Grenada

+
§

America__Guadeloupe

America/Guadeloupe

+
§

America__Guatemala

America/Guatemala

+
§

America__Guayaquil

America/Guayaquil

+
§

America__Guyana

America/Guyana

+
§

America__Halifax

America/Halifax

+
§

America__Havana

America/Havana

+
§

America__Hermosillo

America/Hermosillo

+
§

America__Indiana__Indianapolis

America/Indiana/Indianapolis

+
§

America__Indiana__Knox

America/Indiana/Knox

+
§

America__Indiana__Marengo

America/Indiana/Marengo

+
§

America__Indiana__Petersburg

America/Indiana/Petersburg

+
§

America__Indiana__Tell_City

America/Indiana/Tell_City

+
§

America__Indiana__Vevay

America/Indiana/Vevay

+
§

America__Indiana__Vincennes

America/Indiana/Vincennes

+
§

America__Indiana__Winamac

America/Indiana/Winamac

+
§

America__Indianapolis

America/Indianapolis

+
§

America__Inuvik

America/Inuvik

+
§

America__Iqaluit

America/Iqaluit

+
§

America__Jamaica

America/Jamaica

+
§

America__Jujuy

America/Jujuy

+
§

America__Juneau

America/Juneau

+
§

America__Kentucky__Louisville

America/Kentucky/Louisville

+
§

America__Kentucky__Monticello

America/Kentucky/Monticello

+
§

America__Knox_IN

America/Knox_IN

+
§

America__Kralendijk

America/Kralendijk

+
§

America__La_Paz

America/La_Paz

+
§

America__Lima

America/Lima

+
§

America__Los_Angeles

America/Los_Angeles

+
§

America__Louisville

America/Louisville

+
§

America__Lower_Princes

America/Lower_Princes

+
§

America__Maceio

America/Maceio

+
§

America__Managua

America/Managua

+
§

America__Manaus

America/Manaus

+
§

America__Marigot

America/Marigot

+
§

America__Martinique

America/Martinique

+
§

America__Matamoros

America/Matamoros

+
§

America__Mazatlan

America/Mazatlan

+
§

America__Mendoza

America/Mendoza

+
§

America__Menominee

America/Menominee

+
§

America__Merida

America/Merida

+
§

America__Metlakatla

America/Metlakatla

+
§

America__Mexico_City

America/Mexico_City

+
§

America__Miquelon

America/Miquelon

+
§

America__Moncton

America/Moncton

+
§

America__Monterrey

America/Monterrey

+
§

America__Montevideo

America/Montevideo

+
§

America__Montreal

America/Montreal

+
§

America__Montserrat

America/Montserrat

+
§

America__Nassau

America/Nassau

+
§

America__New_York

America/New_York

+
§

America__Nipigon

America/Nipigon

+
§

America__Nome

America/Nome

+
§

America__Noronha

America/Noronha

+
§

America__North_Dakota__Beulah

America/North_Dakota/Beulah

+
§

America__North_Dakota__Center

America/North_Dakota/Center

+
§

America__North_Dakota__New_Salem

America/North_Dakota/New_Salem

+
§

America__Nuuk

America/Nuuk

+
§

America__Ojinaga

America/Ojinaga

+
§

America__Panama

America/Panama

+
§

America__Pangnirtung

America/Pangnirtung

+
§

America__Paramaribo

America/Paramaribo

+
§

America__Phoenix

America/Phoenix

+
§

America__PortauPrince

America/Port-au-Prince

+
§

America__Port_of_Spain

America/Port_of_Spain

+
§

America__Porto_Acre

America/Porto_Acre

+
§

America__Porto_Velho

America/Porto_Velho

+
§

America__Puerto_Rico

America/Puerto_Rico

+
§

America__Punta_Arenas

America/Punta_Arenas

+
§

America__Rainy_River

America/Rainy_River

+
§

America__Rankin_Inlet

America/Rankin_Inlet

+
§

America__Recife

America/Recife

+
§

America__Regina

America/Regina

+
§

America__Resolute

America/Resolute

+
§

America__Rio_Branco

America/Rio_Branco

+
§

America__Rosario

America/Rosario

+
§

America__Santa_Isabel

America/Santa_Isabel

+
§

America__Santarem

America/Santarem

+
§

America__Santiago

America/Santiago

+
§

America__Santo_Domingo

America/Santo_Domingo

+
§

America__Sao_Paulo

America/Sao_Paulo

+
§

America__Scoresbysund

America/Scoresbysund

+
§

America__Shiprock

America/Shiprock

+
§

America__Sitka

America/Sitka

+
§

America__St_Barthelemy

America/St_Barthelemy

+
§

America__St_Johns

America/St_Johns

+
§

America__St_Kitts

America/St_Kitts

+
§

America__St_Lucia

America/St_Lucia

+
§

America__St_Thomas

America/St_Thomas

+
§

America__St_Vincent

America/St_Vincent

+
§

America__Swift_Current

America/Swift_Current

+
§

America__Tegucigalpa

America/Tegucigalpa

+
§

America__Thule

America/Thule

+
§

America__Thunder_Bay

America/Thunder_Bay

+
§

America__Tijuana

America/Tijuana

+
§

America__Toronto

America/Toronto

+
§

America__Tortola

America/Tortola

+
§

America__Vancouver

America/Vancouver

+
§

America__Virgin

America/Virgin

+
§

America__Whitehorse

America/Whitehorse

+
§

America__Winnipeg

America/Winnipeg

+
§

America__Yakutat

America/Yakutat

+
§

America__Yellowknife

America/Yellowknife

+
§

Antarctica__Casey

Antarctica/Casey

+
§

Antarctica__Davis

Antarctica/Davis

+
§

Antarctica__DumontDUrville

Antarctica/DumontDUrville

+
§

Antarctica__Macquarie

Antarctica/Macquarie

+
§

Antarctica__Mawson

Antarctica/Mawson

+
§

Antarctica__McMurdo

Antarctica/McMurdo

+
§

Antarctica__Palmer

Antarctica/Palmer

+
§

Antarctica__Rothera

Antarctica/Rothera

+
§

Antarctica__South_Pole

Antarctica/South_Pole

+
§

Antarctica__Syowa

Antarctica/Syowa

+
§

Antarctica__Troll

Antarctica/Troll

+
§

Antarctica__Vostok

Antarctica/Vostok

+
§

Arctic__Longyearbyen

Arctic/Longyearbyen

+
§

Asia__Aden

Asia/Aden

+
§

Asia__Almaty

Asia/Almaty

+
§

Asia__Amman

Asia/Amman

+
§

Asia__Anadyr

Asia/Anadyr

+
§

Asia__Aqtau

Asia/Aqtau

+
§

Asia__Aqtobe

Asia/Aqtobe

+
§

Asia__Ashgabat

Asia/Ashgabat

+
§

Asia__Ashkhabad

Asia/Ashkhabad

+
§

Asia__Atyrau

Asia/Atyrau

+
§

Asia__Baghdad

Asia/Baghdad

+
§

Asia__Bahrain

Asia/Bahrain

+
§

Asia__Baku

Asia/Baku

+
§

Asia__Bangkok

Asia/Bangkok

+
§

Asia__Barnaul

Asia/Barnaul

+
§

Asia__Beirut

Asia/Beirut

+
§

Asia__Bishkek

Asia/Bishkek

+
§

Asia__Brunei

Asia/Brunei

+
§

Asia__Calcutta

Asia/Calcutta

+
§

Asia__Chita

Asia/Chita

+
§

Asia__Choibalsan

Asia/Choibalsan

+
§

Asia__Chongqing

Asia/Chongqing

+
§

Asia__Chungking

Asia/Chungking

+
§

Asia__Colombo

Asia/Colombo

+
§

Asia__Dacca

Asia/Dacca

+
§

Asia__Damascus

Asia/Damascus

+
§

Asia__Dhaka

Asia/Dhaka

+
§

Asia__Dili

Asia/Dili

+
§

Asia__Dubai

Asia/Dubai

+
§

Asia__Dushanbe

Asia/Dushanbe

+
§

Asia__Famagusta

Asia/Famagusta

+
§

Asia__Gaza

Asia/Gaza

+
§

Asia__Harbin

Asia/Harbin

+
§

Asia__Hebron

Asia/Hebron

+
§

Asia__Ho_Chi_Minh

Asia/Ho_Chi_Minh

+
§

Asia__Hong_Kong

Asia/Hong_Kong

+
§

Asia__Hovd

Asia/Hovd

+
§

Asia__Irkutsk

Asia/Irkutsk

+
§

Asia__Istanbul

Asia/Istanbul

+
§

Asia__Jakarta

Asia/Jakarta

+
§

Asia__Jayapura

Asia/Jayapura

+
§

Asia__Jerusalem

Asia/Jerusalem

+
§

Asia__Kabul

Asia/Kabul

+
§

Asia__Kamchatka

Asia/Kamchatka

+
§

Asia__Karachi

Asia/Karachi

+
§

Asia__Kashgar

Asia/Kashgar

+
§

Asia__Kathmandu

Asia/Kathmandu

+
§

Asia__Katmandu

Asia/Katmandu

+
§

Asia__Khandyga

Asia/Khandyga

+
§

Asia__Kolkata

Asia/Kolkata

+
§

Asia__Krasnoyarsk

Asia/Krasnoyarsk

+
§

Asia__Kuala_Lumpur

Asia/Kuala_Lumpur

+
§

Asia__Kuching

Asia/Kuching

+
§

Asia__Kuwait

Asia/Kuwait

+
§

Asia__Macao

Asia/Macao

+
§

Asia__Macau

Asia/Macau

+
§

Asia__Magadan

Asia/Magadan

+
§

Asia__Makassar

Asia/Makassar

+
§

Asia__Manila

Asia/Manila

+
§

Asia__Muscat

Asia/Muscat

+
§

Asia__Nicosia

Asia/Nicosia

+
§

Asia__Novokuznetsk

Asia/Novokuznetsk

+
§

Asia__Novosibirsk

Asia/Novosibirsk

+
§

Asia__Omsk

Asia/Omsk

+
§

Asia__Oral

Asia/Oral

+
§

Asia__Phnom_Penh

Asia/Phnom_Penh

+
§

Asia__Pontianak

Asia/Pontianak

+
§

Asia__Pyongyang

Asia/Pyongyang

+
§

Asia__Qatar

Asia/Qatar

+
§

Asia__Qostanay

Asia/Qostanay

+
§

Asia__Qyzylorda

Asia/Qyzylorda

+
§

Asia__Rangoon

Asia/Rangoon

+
§

Asia__Riyadh

Asia/Riyadh

+
§

Asia__Saigon

Asia/Saigon

+
§

Asia__Sakhalin

Asia/Sakhalin

+
§

Asia__Samarkand

Asia/Samarkand

+
§

Asia__Seoul

Asia/Seoul

+
§

Asia__Shanghai

Asia/Shanghai

+
§

Asia__Singapore

Asia/Singapore

+
§

Asia__Srednekolymsk

Asia/Srednekolymsk

+
§

Asia__Taipei

Asia/Taipei

+
§

Asia__Tashkent

Asia/Tashkent

+
§

Asia__Tbilisi

Asia/Tbilisi

+
§

Asia__Tehran

Asia/Tehran

+
§

Asia__Tel_Aviv

Asia/Tel_Aviv

+
§

Asia__Thimbu

Asia/Thimbu

+
§

Asia__Thimphu

Asia/Thimphu

+
§

Asia__Tokyo

Asia/Tokyo

+
§

Asia__Tomsk

Asia/Tomsk

+
§

Asia__Ujung_Pandang

Asia/Ujung_Pandang

+
§

Asia__Ulaanbaatar

Asia/Ulaanbaatar

+
§

Asia__Ulan_Bator

Asia/Ulan_Bator

+
§

Asia__Urumqi

Asia/Urumqi

+
§

Asia__UstNera

Asia/Ust-Nera

+
§

Asia__Vientiane

Asia/Vientiane

+
§

Asia__Vladivostok

Asia/Vladivostok

+
§

Asia__Yakutsk

Asia/Yakutsk

+
§

Asia__Yangon

Asia/Yangon

+
§

Asia__Yekaterinburg

Asia/Yekaterinburg

+
§

Asia__Yerevan

Asia/Yerevan

+
§

Atlantic__Azores

Atlantic/Azores

+
§

Atlantic__Bermuda

Atlantic/Bermuda

+
§

Atlantic__Canary

Atlantic/Canary

+
§

Atlantic__Cape_Verde

Atlantic/Cape_Verde

+
§

Atlantic__Faeroe

Atlantic/Faeroe

+
§

Atlantic__Faroe

Atlantic/Faroe

+
§

Atlantic__Jan_Mayen

Atlantic/Jan_Mayen

+
§

Atlantic__Madeira

Atlantic/Madeira

+
§

Atlantic__Reykjavik

Atlantic/Reykjavik

+
§

Atlantic__South_Georgia

Atlantic/South_Georgia

+
§

Atlantic__St_Helena

Atlantic/St_Helena

+
§

Atlantic__Stanley

Atlantic/Stanley

+
§

Australia__ACT

Australia/ACT

+
§

Australia__Adelaide

Australia/Adelaide

+
§

Australia__Brisbane

Australia/Brisbane

+
§

Australia__Broken_Hill

Australia/Broken_Hill

+
§

Australia__Canberra

Australia/Canberra

+
§

Australia__Currie

Australia/Currie

+
§

Australia__Darwin

Australia/Darwin

+
§

Australia__Eucla

Australia/Eucla

+
§

Australia__Hobart

Australia/Hobart

+
§

Australia__LHI

Australia/LHI

+
§

Australia__Lindeman

Australia/Lindeman

+
§

Australia__Lord_Howe

Australia/Lord_Howe

+
§

Australia__Melbourne

Australia/Melbourne

+
§

Australia__NSW

Australia/NSW

+
§

Australia__North

Australia/North

+
§

Australia__Perth

Australia/Perth

+
§

Australia__Queensland

Australia/Queensland

+
§

Australia__South

Australia/South

+
§

Australia__Sydney

Australia/Sydney

+
§

Australia__Tasmania

Australia/Tasmania

+
§

Australia__Victoria

Australia/Victoria

+
§

Australia__West

Australia/West

+
§

Australia__Yancowinna

Australia/Yancowinna

+
§

Brazil__Acre

Brazil/Acre

+
§

Brazil__DeNoronha

Brazil/DeNoronha

+
§

Brazil__East

Brazil/East

+
§

Brazil__West

Brazil/West

+
§

CET

CET

+
§

CST6CDT

CST6CDT

+
§

Canada__Atlantic

Canada/Atlantic

+
§

Canada__Central

Canada/Central

+
§

Canada__Eastern

Canada/Eastern

+
§

Canada__Mountain

Canada/Mountain

+
§

Canada__Newfoundland

Canada/Newfoundland

+
§

Canada__Pacific

Canada/Pacific

+
§

Canada__Saskatchewan

Canada/Saskatchewan

+
§

Canada__Yukon

Canada/Yukon

+
§

Chile__Continental

Chile/Continental

+
§

Chile__EasterIsland

Chile/EasterIsland

+
§

Cuba

Cuba

+
§

EET

EET

+
§

EST

EST

+
§

EST5EDT

EST5EDT

+
§

Egypt

Egypt

+
§

Eire

Eire

+
§

Etc__GMT

Etc/GMT

+
§

Etc__GMTPlus0

Etc/GMT+0

+
§

Etc__GMTPlus1

Etc/GMT+1

+
§

Etc__GMTPlus10

Etc/GMT+10

+
§

Etc__GMTPlus11

Etc/GMT+11

+
§

Etc__GMTPlus12

Etc/GMT+12

+
§

Etc__GMTPlus2

Etc/GMT+2

+
§

Etc__GMTPlus3

Etc/GMT+3

+
§

Etc__GMTPlus4

Etc/GMT+4

+
§

Etc__GMTPlus5

Etc/GMT+5

+
§

Etc__GMTPlus6

Etc/GMT+6

+
§

Etc__GMTPlus7

Etc/GMT+7

+
§

Etc__GMTPlus8

Etc/GMT+8

+
§

Etc__GMTPlus9

Etc/GMT+9

+
§

Etc__GMTMinus0

Etc/GMT-0

+
§

Etc__GMTMinus1

Etc/GMT-1

+
§

Etc__GMTMinus10

Etc/GMT-10

+
§

Etc__GMTMinus11

Etc/GMT-11

+
§

Etc__GMTMinus12

Etc/GMT-12

+
§

Etc__GMTMinus13

Etc/GMT-13

+
§

Etc__GMTMinus14

Etc/GMT-14

+
§

Etc__GMTMinus2

Etc/GMT-2

+
§

Etc__GMTMinus3

Etc/GMT-3

+
§

Etc__GMTMinus4

Etc/GMT-4

+
§

Etc__GMTMinus5

Etc/GMT-5

+
§

Etc__GMTMinus6

Etc/GMT-6

+
§

Etc__GMTMinus7

Etc/GMT-7

+
§

Etc__GMTMinus8

Etc/GMT-8

+
§

Etc__GMTMinus9

Etc/GMT-9

+
§

Etc__GMT0

Etc/GMT0

+
§

Etc__Greenwich

Etc/Greenwich

+
§

Etc__UCT

Etc/UCT

+
§

Etc__UTC

Etc/UTC

+
§

Etc__Universal

Etc/Universal

+
§

Etc__Zulu

Etc/Zulu

+
§

Europe__Amsterdam

Europe/Amsterdam

+
§

Europe__Andorra

Europe/Andorra

+
§

Europe__Astrakhan

Europe/Astrakhan

+
§

Europe__Athens

Europe/Athens

+
§

Europe__Belfast

Europe/Belfast

+
§

Europe__Belgrade

Europe/Belgrade

+
§

Europe__Berlin

Europe/Berlin

+
§

Europe__Bratislava

Europe/Bratislava

+
§

Europe__Brussels

Europe/Brussels

+
§

Europe__Bucharest

Europe/Bucharest

+
§

Europe__Budapest

Europe/Budapest

+
§

Europe__Busingen

Europe/Busingen

+
§

Europe__Chisinau

Europe/Chisinau

+
§

Europe__Copenhagen

Europe/Copenhagen

+
§

Europe__Dublin

Europe/Dublin

+
§

Europe__Gibraltar

Europe/Gibraltar

+
§

Europe__Guernsey

Europe/Guernsey

+
§

Europe__Helsinki

Europe/Helsinki

+
§

Europe__Isle_of_Man

Europe/Isle_of_Man

+
§

Europe__Istanbul

Europe/Istanbul

+
§

Europe__Jersey

Europe/Jersey

+
§

Europe__Kaliningrad

Europe/Kaliningrad

+
§

Europe__Kiev

Europe/Kiev

+
§

Europe__Kirov

Europe/Kirov

+
§

Europe__Kyiv

Europe/Kyiv

+
§

Europe__Lisbon

Europe/Lisbon

+
§

Europe__Ljubljana

Europe/Ljubljana

+
§

Europe__London

Europe/London

+
§

Europe__Luxembourg

Europe/Luxembourg

+
§

Europe__Madrid

Europe/Madrid

+
§

Europe__Malta

Europe/Malta

+
§

Europe__Mariehamn

Europe/Mariehamn

+
§

Europe__Minsk

Europe/Minsk

+
§

Europe__Monaco

Europe/Monaco

+
§

Europe__Moscow

Europe/Moscow

+
§

Europe__Nicosia

Europe/Nicosia

+
§

Europe__Oslo

Europe/Oslo

+
§

Europe__Paris

Europe/Paris

+
§

Europe__Podgorica

Europe/Podgorica

+
§

Europe__Prague

Europe/Prague

+
§

Europe__Riga

Europe/Riga

+
§

Europe__Rome

Europe/Rome

+
§

Europe__Samara

Europe/Samara

+
§

Europe__San_Marino

Europe/San_Marino

+
§

Europe__Sarajevo

Europe/Sarajevo

+
§

Europe__Saratov

Europe/Saratov

+
§

Europe__Simferopol

Europe/Simferopol

+
§

Europe__Skopje

Europe/Skopje

+
§

Europe__Sofia

Europe/Sofia

+
§

Europe__Stockholm

Europe/Stockholm

+
§

Europe__Tallinn

Europe/Tallinn

+
§

Europe__Tirane

Europe/Tirane

+
§

Europe__Tiraspol

Europe/Tiraspol

+
§

Europe__Ulyanovsk

Europe/Ulyanovsk

+
§

Europe__Uzhgorod

Europe/Uzhgorod

+
§

Europe__Vaduz

Europe/Vaduz

+
§

Europe__Vatican

Europe/Vatican

+
§

Europe__Vienna

Europe/Vienna

+
§

Europe__Vilnius

Europe/Vilnius

+
§

Europe__Volgograd

Europe/Volgograd

+
§

Europe__Warsaw

Europe/Warsaw

+
§

Europe__Zagreb

Europe/Zagreb

+
§

Europe__Zaporozhye

Europe/Zaporozhye

+
§

Europe__Zurich

Europe/Zurich

+
§

GB

GB

+
§

GBEire

GB-Eire

+
§

GMT

GMT

+
§

GMTPlus0

GMT+0

+
§

GMTMinus0

GMT-0

+
§

GMT0

GMT0

+
§

Greenwich

Greenwich

+
§

HST

HST

+
§

Hongkong

Hongkong

+
§

Iceland

Iceland

+
§

Indian__Antananarivo

Indian/Antananarivo

+
§

Indian__Chagos

Indian/Chagos

+
§

Indian__Christmas

Indian/Christmas

+
§

Indian__Cocos

Indian/Cocos

+
§

Indian__Comoro

Indian/Comoro

+
§

Indian__Kerguelen

Indian/Kerguelen

+
§

Indian__Mahe

Indian/Mahe

+
§

Indian__Maldives

Indian/Maldives

+
§

Indian__Mauritius

Indian/Mauritius

+
§

Indian__Mayotte

Indian/Mayotte

+
§

Indian__Reunion

Indian/Reunion

+
§

Iran

Iran

+
§

Israel

Israel

+
§

Jamaica

Jamaica

+
§

Japan

Japan

+
§

Kwajalein

Kwajalein

+
§

Libya

Libya

+
§

MET

MET

+
§

MST

MST

+
§

MST7MDT

MST7MDT

+
§

Mexico__BajaNorte

Mexico/BajaNorte

+
§

Mexico__BajaSur

Mexico/BajaSur

+
§

Mexico__General

Mexico/General

+
§

NZ

NZ

+
§

NZCHAT

NZ-CHAT

+
§

Navajo

Navajo

+
§

PRC

PRC

+
§

PST8PDT

PST8PDT

+
§

Pacific__Apia

Pacific/Apia

+
§

Pacific__Auckland

Pacific/Auckland

+
§

Pacific__Bougainville

Pacific/Bougainville

+
§

Pacific__Chatham

Pacific/Chatham

+
§

Pacific__Chuuk

Pacific/Chuuk

+
§

Pacific__Easter

Pacific/Easter

+
§

Pacific__Efate

Pacific/Efate

+
§

Pacific__Enderbury

Pacific/Enderbury

+
§

Pacific__Fakaofo

Pacific/Fakaofo

+
§

Pacific__Fiji

Pacific/Fiji

+
§

Pacific__Funafuti

Pacific/Funafuti

+
§

Pacific__Galapagos

Pacific/Galapagos

+
§

Pacific__Gambier

Pacific/Gambier

+
§

Pacific__Guadalcanal

Pacific/Guadalcanal

+
§

Pacific__Guam

Pacific/Guam

+
§

Pacific__Honolulu

Pacific/Honolulu

+
§

Pacific__Johnston

Pacific/Johnston

+
§

Pacific__Kanton

Pacific/Kanton

+
§

Pacific__Kiritimati

Pacific/Kiritimati

+
§

Pacific__Kosrae

Pacific/Kosrae

+
§

Pacific__Kwajalein

Pacific/Kwajalein

+
§

Pacific__Majuro

Pacific/Majuro

+
§

Pacific__Marquesas

Pacific/Marquesas

+
§

Pacific__Midway

Pacific/Midway

+
§

Pacific__Nauru

Pacific/Nauru

+
§

Pacific__Niue

Pacific/Niue

+
§

Pacific__Norfolk

Pacific/Norfolk

+
§

Pacific__Noumea

Pacific/Noumea

+
§

Pacific__Pago_Pago

Pacific/Pago_Pago

+
§

Pacific__Palau

Pacific/Palau

+
§

Pacific__Pitcairn

Pacific/Pitcairn

+
§

Pacific__Pohnpei

Pacific/Pohnpei

+
§

Pacific__Ponape

Pacific/Ponape

+
§

Pacific__Port_Moresby

Pacific/Port_Moresby

+
§

Pacific__Rarotonga

Pacific/Rarotonga

+
§

Pacific__Saipan

Pacific/Saipan

+
§

Pacific__Samoa

Pacific/Samoa

+
§

Pacific__Tahiti

Pacific/Tahiti

+
§

Pacific__Tarawa

Pacific/Tarawa

+
§

Pacific__Tongatapu

Pacific/Tongatapu

+
§

Pacific__Truk

Pacific/Truk

+
§

Pacific__Wake

Pacific/Wake

+
§

Pacific__Wallis

Pacific/Wallis

+
§

Pacific__Yap

Pacific/Yap

+
§

Poland

Poland

+
§

Portugal

Portugal

+
§

ROC

ROC

+
§

ROK

ROK

+
§

Singapore

Singapore

+
§

Turkey

Turkey

+
§

UCT

UCT

+
§

US__Alaska

US/Alaska

+
§

US__Aleutian

US/Aleutian

+
§

US__Arizona

US/Arizona

+
§

US__Central

US/Central

+
§

US__EastIndiana

US/East-Indiana

+
§

US__Eastern

US/Eastern

+
§

US__Hawaii

US/Hawaii

+
§

US__IndianaStarke

US/Indiana-Starke

+
§

US__Michigan

US/Michigan

+
§

US__Mountain

US/Mountain

+
§

US__Pacific

US/Pacific

+
§

US__Samoa

US/Samoa

+
§

UTC

UTC

+
§

Universal

Universal

+
§

WSU

W-SU

+
§

WET

WET

+
§

Zulu

Zulu

+

Implementations§

§

impl Tz

pub fn name(self) -> &'static str

Trait Implementations§

§

impl Clone for Tz

§

fn clone(&self) -> Tz

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for Tz

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl Display for Tz

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl FromStr for Tz

§

type Err = String

The associated error which can be returned from parsing.
§

fn from_str(s: &str) -> Result<Tz, <Tz as FromStr>::Err>

Parses a string s to return a value of this type. Read more
§

impl Hash for Tz

§

fn hash<__H>(&self, state: &mut __H)where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
§

impl PartialEq for Tz

§

fn eq(&self, other: &Tz) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
§

impl TimeZone for Tz

§

type Offset = TzOffset

An associated offset type. +This type is used to store the actual offset in date and time types. +The original TimeZone value can be recovered via TimeZone::from_offset.
§

fn from_offset(offset: &<Tz as TimeZone>::Offset) -> Tz

Reconstructs the time zone from the offset.
§

fn offset_from_local_date( + &self, + local: &NaiveDate +) -> LocalResult<<Tz as TimeZone>::Offset>

Creates the offset(s) for given local NaiveDate if possible.
§

fn offset_from_local_datetime( + &self, + local: &NaiveDateTime +) -> LocalResult<<Tz as TimeZone>::Offset>

Creates the offset(s) for given local NaiveDateTime if possible.
§

fn offset_from_utc_date(&self, utc: &NaiveDate) -> <Tz as TimeZone>::Offset

Creates the offset for given UTC NaiveDate. This cannot fail.
§

fn offset_from_utc_datetime( + &self, + utc: &NaiveDateTime +) -> <Tz as TimeZone>::Offset

Creates the offset for given UTC NaiveDateTime. This cannot fail.
source§

fn with_ymd_and_hms( + &self, + year: i32, + month: u32, + day: u32, + hour: u32, + min: u32, + sec: u32 +) -> LocalResult<DateTime<Self>>

Make a new DateTime from year, month, day, time components and current time zone. Read more
source§

fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self>

👎Deprecated since 0.4.23: use with_ymd_and_hms() instead
Makes a new Date from year, month, day and the current time zone. +This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. Read more
source§

fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>>

👎Deprecated since 0.4.23: use with_ymd_and_hms() instead
Makes a new Date from year, month, day and the current time zone. +This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. Read more
source§

fn yo(&self, year: i32, ordinal: u32) -> Date<Self>

👎Deprecated since 0.4.23: use from_local_datetime() with a NaiveDateTime instead
Makes a new Date from year, day of year (DOY or “ordinal”) and the current time zone. +This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. Read more
source§

fn yo_opt(&self, year: i32, ordinal: u32) -> LocalResult<Date<Self>>

👎Deprecated since 0.4.23: use from_local_datetime() with a NaiveDateTime instead
Makes a new Date from year, day of year (DOY or “ordinal”) and the current time zone. +This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. Read more
source§

fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self>

👎Deprecated since 0.4.23: use from_local_datetime() with a NaiveDateTime instead
Makes a new Date from ISO week date (year and week number), day of the week (DOW) and +the current time zone. +This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. +The resulting Date may have a different year from the input year. Read more
source§

fn isoywd_opt( + &self, + year: i32, + week: u32, + weekday: Weekday +) -> LocalResult<Date<Self>>

👎Deprecated since 0.4.23: use from_local_datetime() with a NaiveDateTime instead
Makes a new Date from ISO week date (year and week number), day of the week (DOW) and +the current time zone. +This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. +The resulting Date may have a different year from the input year. Read more
source§

fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self>

👎Deprecated since 0.4.23: use timestamp_opt() instead
Makes a new DateTime from the number of non-leap seconds +since January 1, 1970 0:00:00 UTC (aka “UNIX timestamp”) +and the number of nanoseconds since the last whole non-leap second. Read more
source§

fn timestamp_opt(&self, secs: i64, nsecs: u32) -> LocalResult<DateTime<Self>>

Makes a new DateTime from the number of non-leap seconds +since January 1, 1970 0:00:00 UTC (aka “UNIX timestamp”) +and the number of nanoseconds since the last whole non-leap second. Read more
source§

fn timestamp_millis(&self, millis: i64) -> DateTime<Self>

👎Deprecated since 0.4.23: use timestamp_millis_opt() instead
Makes a new DateTime from the number of non-leap milliseconds +since January 1, 1970 0:00:00 UTC (aka “UNIX timestamp”). Read more
source§

fn timestamp_millis_opt(&self, millis: i64) -> LocalResult<DateTime<Self>>

Makes a new DateTime from the number of non-leap milliseconds +since January 1, 1970 0:00:00 UTC (aka “UNIX timestamp”). Read more
source§

fn timestamp_nanos(&self, nanos: i64) -> DateTime<Self>

Makes a new DateTime from the number of non-leap nanoseconds +since January 1, 1970 0:00:00 UTC (aka “UNIX timestamp”). Read more
source§

fn timestamp_micros(&self, micros: i64) -> LocalResult<DateTime<Self>>

Makes a new DateTime from the number of non-leap microseconds +since January 1, 1970 0:00:00 UTC (aka “UNIX timestamp”). Read more
source§

fn datetime_from_str( + &self, + s: &str, + fmt: &str +) -> Result<DateTime<Self>, ParseError>

👎Deprecated since 0.4.29: use DateTime::parse_from_str instead
Parses a string with the specified format string and returns a +DateTime with the current offset. Read more
source§

fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>>

👎Deprecated since 0.4.23: use from_local_datetime() instead
Converts the local NaiveDate to the timezone-aware Date if possible.
source§

fn from_local_datetime( + &self, + local: &NaiveDateTime +) -> LocalResult<DateTime<Self>>

Converts the local NaiveDateTime to the timezone-aware DateTime if possible.
source§

fn from_utc_date(&self, utc: &NaiveDate) -> Date<Self>

👎Deprecated since 0.4.23: use from_utc_datetime() instead
Converts the UTC NaiveDate to the local time. +The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
source§

fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Self>

Converts the UTC NaiveDateTime to the local time. +The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
§

impl Copy for Tz

§

impl Eq for Tz

§

impl StructuralEq for Tz

§

impl StructuralPartialEq for Tz

Auto Trait Implementations§

§

impl RefUnwindSafe for Tz

§

impl Send for Tz

§

impl Sync for Tz

§

impl Unpin for Tz

§

impl UnwindSafe for Tz

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/new_empty/index.html b/ironcalc_base/new_empty/index.html new file mode 100644 index 0000000..218c4d1 --- /dev/null +++ b/ironcalc_base/new_empty/index.html @@ -0,0 +1 @@ +ironcalc_base::new_empty - Rust

Enums

  • TimeZones built at compile time from the tz database

Constants

\ No newline at end of file diff --git a/ironcalc_base/new_empty/sidebar-items.js b/ironcalc_base/new_empty/sidebar-items.js new file mode 100644 index 0000000..d915348 --- /dev/null +++ b/ironcalc_base/new_empty/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"constant":["APPLICATION","APP_VERSION","IRONCALC_USER"],"enum":["Tz"]}; \ No newline at end of file diff --git a/ironcalc_base/number_format/fn.format_number.html b/ironcalc_base/number_format/fn.format_number.html new file mode 100644 index 0000000..25ee273 --- /dev/null +++ b/ironcalc_base/number_format/fn.format_number.html @@ -0,0 +1 @@ +format_number in ironcalc_base::number_format - Rust
pub fn format_number(value: f64, format_code: &str, locale: &str) -> Formatted
\ No newline at end of file diff --git a/ironcalc_base/number_format/fn.get_default_num_fmt_id.html b/ironcalc_base/number_format/fn.get_default_num_fmt_id.html new file mode 100644 index 0000000..04c33b2 --- /dev/null +++ b/ironcalc_base/number_format/fn.get_default_num_fmt_id.html @@ -0,0 +1 @@ +get_default_num_fmt_id in ironcalc_base::number_format - Rust
pub fn get_default_num_fmt_id(num_fmt: &str) -> Option<i32>
\ No newline at end of file diff --git a/ironcalc_base/number_format/fn.get_new_num_fmt_index.html b/ironcalc_base/number_format/fn.get_new_num_fmt_index.html new file mode 100644 index 0000000..18facd0 --- /dev/null +++ b/ironcalc_base/number_format/fn.get_new_num_fmt_index.html @@ -0,0 +1 @@ +get_new_num_fmt_index in ironcalc_base::number_format - Rust
pub fn get_new_num_fmt_index(num_fmts: &[NumFmt]) -> i32
\ No newline at end of file diff --git a/ironcalc_base/number_format/fn.get_num_fmt.html b/ironcalc_base/number_format/fn.get_num_fmt.html new file mode 100644 index 0000000..c401983 --- /dev/null +++ b/ironcalc_base/number_format/fn.get_num_fmt.html @@ -0,0 +1 @@ +get_num_fmt in ironcalc_base::number_format - Rust
pub fn get_num_fmt(num_fmt_id: i32, num_fmts: &[NumFmt]) -> String
\ No newline at end of file diff --git a/ironcalc_base/number_format/fn.to_excel_precision_str.html b/ironcalc_base/number_format/fn.to_excel_precision_str.html new file mode 100644 index 0000000..1bd1dff --- /dev/null +++ b/ironcalc_base/number_format/fn.to_excel_precision_str.html @@ -0,0 +1,9 @@ +to_excel_precision_str in ironcalc_base::number_format - Rust
pub fn to_excel_precision_str(value: f64) -> String
Expand description

It rounds a f64 with p significant figures:

+ +
    use ironcalc_base::number_format;
+    assert_eq!(number_format::to_precision(0.1+0.2, 15), 0.3);
+    assert_eq!(number_format::to_excel_precision_str(0.1+0.2), "0.3");
+

This intends to be equivalent to the js: ${parseFloat(value.toPrecision(precision)}) +See (ecma). +FIXME: There has to be a better algorithm :/

+
\ No newline at end of file diff --git a/ironcalc_base/number_format/fn.to_precision.html b/ironcalc_base/number_format/fn.to_precision.html new file mode 100644 index 0000000..28dac57 --- /dev/null +++ b/ironcalc_base/number_format/fn.to_precision.html @@ -0,0 +1 @@ +to_precision in ironcalc_base::number_format - Rust
pub fn to_precision(value: f64, precision: usize) -> f64
\ No newline at end of file diff --git a/ironcalc_base/number_format/fn.to_precision_str.html b/ironcalc_base/number_format/fn.to_precision_str.html new file mode 100644 index 0000000..7fcc8e0 --- /dev/null +++ b/ironcalc_base/number_format/fn.to_precision_str.html @@ -0,0 +1 @@ +to_precision_str in ironcalc_base::number_format - Rust
pub fn to_precision_str(value: f64, precision: usize) -> String
\ No newline at end of file diff --git a/ironcalc_base/number_format/index.html b/ironcalc_base/number_format/index.html new file mode 100644 index 0000000..2b24491 --- /dev/null +++ b/ironcalc_base/number_format/index.html @@ -0,0 +1 @@ +ironcalc_base::number_format - Rust
\ No newline at end of file diff --git a/ironcalc_base/number_format/sidebar-items.js b/ironcalc_base/number_format/sidebar-items.js new file mode 100644 index 0000000..0b2b594 --- /dev/null +++ b/ironcalc_base/number_format/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["format_number","get_default_num_fmt_id","get_new_num_fmt_index","get_num_fmt","to_excel_precision_str","to_precision","to_precision_str"]}; \ No newline at end of file diff --git a/ironcalc_base/sidebar-items.js b/ironcalc_base/sidebar-items.js new file mode 100644 index 0000000..1c1d3f4 --- /dev/null +++ b/ironcalc_base/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["calc_result","cell","expressions","formatter","language","locale","model","new_empty","number_format","types","worksheet"]}; \ No newline at end of file diff --git a/ironcalc_base/types/enum.BorderStyle.html b/ironcalc_base/types/enum.BorderStyle.html new file mode 100644 index 0000000..a34e5c9 --- /dev/null +++ b/ironcalc_base/types/enum.BorderStyle.html @@ -0,0 +1,29 @@ +BorderStyle in ironcalc_base::types - Rust
pub enum BorderStyle {
+    Thin,
+    Medium,
+    Thick,
+    Double,
+    Dotted,
+    SlantDashDot,
+    MediumDashed,
+    MediumDashDotDot,
+    MediumDashDot,
+}

Variants§

§

Thin

§

Medium

§

Thick

§

Double

§

Dotted

§

SlantDashDot

§

MediumDashed

§

MediumDashDotDot

§

MediumDashDot

Trait Implementations§

source§

impl Clone for BorderStyle

source§

fn clone(&self) -> BorderStyle

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for BorderStyle

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for BorderStyle

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Display for BorderStyle

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for BorderStyle

source§

fn eq(&self, other: &BorderStyle) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for BorderStyle

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for BorderStyle

source§

impl StructuralEq for BorderStyle

source§

impl StructuralPartialEq for BorderStyle

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/enum.Cell.html b/ironcalc_base/types/enum.Cell.html new file mode 100644 index 0000000..9c6f2ba --- /dev/null +++ b/ironcalc_base/types/enum.Cell.html @@ -0,0 +1,76 @@ +Cell in ironcalc_base::types - Rust
pub enum Cell {
+    EmptyCell {
+        s: i32,
+    },
+    BooleanCell {
+        v: bool,
+        s: i32,
+    },
+    NumberCell {
+        v: f64,
+        s: i32,
+    },
+    ErrorCell {
+        ei: Error,
+        s: i32,
+    },
+    SharedString {
+        si: i32,
+        s: i32,
+    },
+    CellFormula {
+        f: i32,
+        s: i32,
+    },
+    CellFormulaBoolean {
+        f: i32,
+        v: bool,
+        s: i32,
+    },
+    CellFormulaNumber {
+        f: i32,
+        v: f64,
+        s: i32,
+    },
+    CellFormulaString {
+        f: i32,
+        v: String,
+        s: i32,
+    },
+    CellFormulaError {
+        f: i32,
+        ei: Error,
+        s: i32,
+        o: String,
+        m: String,
+    },
+}

Variants§

§

EmptyCell

Fields

§

BooleanCell

Fields

§

NumberCell

Fields

§

ErrorCell

Fields

§

SharedString

Fields

§si: i32
§

CellFormula

Fields

§

CellFormulaBoolean

Fields

§

CellFormulaNumber

Fields

§

CellFormulaString

Fields

§

CellFormulaError

Fields

Implementations§

source§

impl Cell

source

pub fn new_string(si: i32, s: i32) -> Cell

Creates a new Cell with a shared string (si is the string index)

+
source

pub fn new_number(v: f64, s: i32) -> Cell

Creates a new Cell with a number

+
source

pub fn new_boolean(v: bool, s: i32) -> Cell

Creates a new Cell with a boolean

+
source

pub fn new_error(ei: Error, s: i32) -> Cell

Creates a new Cell with an error value

+
source

pub fn new_formula(f: i32, s: i32) -> Cell

Creates a new Cell with an unevaluated formula

+
source

pub fn get_formula(&self) -> Option<i32>

Returns the formula of a cell if any.

+
source

pub fn has_formula(&self) -> bool

source

pub fn set_style(&mut self, style: i32)

source

pub fn get_style(&self) -> i32

source

pub fn get_type(&self) -> CellType

source

pub fn get_text(&self, shared_strings: &[String], language: &Language) -> String

source

pub fn value(&self, shared_strings: &[String], language: &Language) -> CellValue

source

pub fn formatted_value<F>( + &self, + shared_strings: &[String], + language: &Language, + format_number: F +) -> Stringwhere + F: Fn(f64) -> String,

Trait Implementations§

source§

impl Clone for Cell

source§

fn clone(&self) -> Cell

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Cell

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Cell

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for Cell

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Cell

source§

fn eq(&self, other: &Cell) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Cell

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl StructuralPartialEq for Cell

Auto Trait Implementations§

§

impl RefUnwindSafe for Cell

§

impl Send for Cell

§

impl Sync for Cell

§

impl Unpin for Cell

§

impl UnwindSafe for Cell

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/enum.CellType.html b/ironcalc_base/types/enum.CellType.html new file mode 100644 index 0000000..f2a9add --- /dev/null +++ b/ironcalc_base/types/enum.CellType.html @@ -0,0 +1,22 @@ +CellType in ironcalc_base::types - Rust
pub enum CellType {
+    Number = 1,
+    Text = 2,
+    LogicalValue = 4,
+    ErrorValue = 16,
+    Array = 64,
+    CompoundData = 128,
+}
Expand description

Cell type enum matching Excel TYPE() function values.

+

Variants§

§

Number = 1

§

Text = 2

§

LogicalValue = 4

§

ErrorValue = 16

§

Array = 64

§

CompoundData = 128

Trait Implementations§

source§

impl Debug for CellType

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for CellType

source§

fn eq(&self, other: &CellType) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for CellType

source§

impl StructuralEq for CellType

source§

impl StructuralPartialEq for CellType

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/types/enum.FontScheme.html b/ironcalc_base/types/enum.FontScheme.html new file mode 100644 index 0000000..30a42aa --- /dev/null +++ b/ironcalc_base/types/enum.FontScheme.html @@ -0,0 +1,23 @@ +FontScheme in ironcalc_base::types - Rust
pub enum FontScheme {
+    Minor,
+    Major,
+    None,
+}

Variants§

§

Minor

§

Major

§

None

Trait Implementations§

source§

impl Clone for FontScheme

source§

fn clone(&self) -> FontScheme

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for FontScheme

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for FontScheme

source§

fn default() -> FontScheme

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for FontScheme

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Display for FontScheme

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for FontScheme

source§

fn eq(&self, other: &FontScheme) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for FontScheme

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for FontScheme

source§

impl StructuralEq for FontScheme

source§

impl StructuralPartialEq for FontScheme

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/enum.HorizontalAlignment.html b/ironcalc_base/types/enum.HorizontalAlignment.html new file mode 100644 index 0000000..41a477e --- /dev/null +++ b/ironcalc_base/types/enum.HorizontalAlignment.html @@ -0,0 +1,28 @@ +HorizontalAlignment in ironcalc_base::types - Rust
pub enum HorizontalAlignment {
+    Center,
+    CenterContinuous,
+    Distributed,
+    Fill,
+    General,
+    Justify,
+    Left,
+    Right,
+}

Variants§

§

Center

§

CenterContinuous

§

Distributed

§

Fill

§

General

§

Justify

§

Left

§

Right

Trait Implementations§

source§

impl Clone for HorizontalAlignment

source§

fn clone(&self) -> HorizontalAlignment

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for HorizontalAlignment

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for HorizontalAlignment

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for HorizontalAlignment

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Display for HorizontalAlignment

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for HorizontalAlignment

source§

fn eq(&self, other: &HorizontalAlignment) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for HorizontalAlignment

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for HorizontalAlignment

source§

impl StructuralEq for HorizontalAlignment

source§

impl StructuralPartialEq for HorizontalAlignment

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/enum.SheetState.html b/ironcalc_base/types/enum.SheetState.html new file mode 100644 index 0000000..57338f5 --- /dev/null +++ b/ironcalc_base/types/enum.SheetState.html @@ -0,0 +1,29 @@ +SheetState in ironcalc_base::types - Rust
pub enum SheetState {
+    Visible,
+    Hidden,
+    VeryHidden,
+}
Expand description

Internal representation of a worksheet Excel object

+
    +
  • state: +18.18.68 ST_SheetState (Sheet Visibility Types) +hidden, veryHidden, visible
  • +
+

Variants§

§

Visible

§

Hidden

§

VeryHidden

Trait Implementations§

source§

impl Clone for SheetState

source§

fn clone(&self) -> SheetState

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SheetState

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for SheetState

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Display for SheetState

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for SheetState

source§

fn eq(&self, other: &SheetState) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for SheetState

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for SheetState

source§

impl StructuralEq for SheetState

source§

impl StructuralPartialEq for SheetState

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/enum.VerticalAlignment.html b/ironcalc_base/types/enum.VerticalAlignment.html new file mode 100644 index 0000000..4f3e729 --- /dev/null +++ b/ironcalc_base/types/enum.VerticalAlignment.html @@ -0,0 +1,25 @@ +VerticalAlignment in ironcalc_base::types - Rust
pub enum VerticalAlignment {
+    Bottom,
+    Center,
+    Distributed,
+    Justify,
+    Top,
+}

Variants§

§

Bottom

§

Center

§

Distributed

§

Justify

§

Top

Trait Implementations§

source§

impl Clone for VerticalAlignment

source§

fn clone(&self) -> VerticalAlignment

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for VerticalAlignment

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for VerticalAlignment

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for VerticalAlignment

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Display for VerticalAlignment

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for VerticalAlignment

source§

fn eq(&self, other: &VerticalAlignment) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for VerticalAlignment

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for VerticalAlignment

source§

impl StructuralEq for VerticalAlignment

source§

impl StructuralPartialEq for VerticalAlignment

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/index.html b/ironcalc_base/types/index.html new file mode 100644 index 0000000..375cb03 --- /dev/null +++ b/ironcalc_base/types/index.html @@ -0,0 +1,3 @@ +ironcalc_base::types - Rust

Module ironcalc_base::types

source ·

Structs

Enums

Type Aliases

  • Internal representation of Excel’s sheet_data +It is row first and because of this all of our API’s should be row first
\ No newline at end of file diff --git a/ironcalc_base/types/sidebar-items.js b/ironcalc_base/types/sidebar-items.js new file mode 100644 index 0000000..2d18602 --- /dev/null +++ b/ironcalc_base/types/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["BorderStyle","Cell","CellType","FontScheme","HorizontalAlignment","SheetState","VerticalAlignment"],"struct":["Alignment","Border","BorderItem","CellStyleXfs","CellStyles","CellXfs","Col","Comment","DefinedName","Fill","Font","Metadata","NumFmt","Row","SheetInfo","Styles","Table","TableColumn","TableStyleInfo","Workbook","WorkbookSettings","Worksheet"],"type":["SheetData"]}; \ No newline at end of file diff --git a/ironcalc_base/types/struct.Alignment.html b/ironcalc_base/types/struct.Alignment.html new file mode 100644 index 0000000..8daab3b --- /dev/null +++ b/ironcalc_base/types/struct.Alignment.html @@ -0,0 +1,22 @@ +Alignment in ironcalc_base::types - Rust
pub struct Alignment {
+    pub horizontal: HorizontalAlignment,
+    pub vertical: VerticalAlignment,
+    pub wrap_text: bool,
+}

Fields§

§horizontal: HorizontalAlignment§vertical: VerticalAlignment§wrap_text: bool

Trait Implementations§

source§

impl Clone for Alignment

source§

fn clone(&self) -> Alignment

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Alignment

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Alignment

source§

fn default() -> Alignment

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for Alignment

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Alignment

source§

fn eq(&self, other: &Alignment) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Alignment

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for Alignment

source§

impl StructuralEq for Alignment

source§

impl StructuralPartialEq for Alignment

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.Border.html b/ironcalc_base/types/struct.Border.html new file mode 100644 index 0000000..99edce4 --- /dev/null +++ b/ironcalc_base/types/struct.Border.html @@ -0,0 +1,26 @@ +Border in ironcalc_base::types - Rust

Struct ironcalc_base::types::Border

source ·
pub struct Border {
+    pub diagonal_up: bool,
+    pub diagonal_down: bool,
+    pub left: Option<BorderItem>,
+    pub right: Option<BorderItem>,
+    pub top: Option<BorderItem>,
+    pub bottom: Option<BorderItem>,
+    pub diagonal: Option<BorderItem>,
+}

Fields§

§diagonal_up: bool§diagonal_down: bool§left: Option<BorderItem>§right: Option<BorderItem>§top: Option<BorderItem>§bottom: Option<BorderItem>§diagonal: Option<BorderItem>

Trait Implementations§

source§

impl Clone for Border

source§

fn clone(&self) -> Border

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Border

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Border

source§

fn default() -> Border

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for Border

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Border

source§

fn eq(&self, other: &Border) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Border

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for Border

source§

impl StructuralEq for Border

source§

impl StructuralPartialEq for Border

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.BorderItem.html b/ironcalc_base/types/struct.BorderItem.html new file mode 100644 index 0000000..6e1092a --- /dev/null +++ b/ironcalc_base/types/struct.BorderItem.html @@ -0,0 +1,21 @@ +BorderItem in ironcalc_base::types - Rust
pub struct BorderItem {
+    pub style: BorderStyle,
+    pub color: Option<String>,
+}

Fields§

§style: BorderStyle§color: Option<String>

Trait Implementations§

source§

impl Clone for BorderItem

source§

fn clone(&self) -> BorderItem

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for BorderItem

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for BorderItem

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for BorderItem

source§

fn eq(&self, other: &BorderItem) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for BorderItem

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for BorderItem

source§

impl StructuralEq for BorderItem

source§

impl StructuralPartialEq for BorderItem

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.CellStyleXfs.html b/ironcalc_base/types/struct.CellStyleXfs.html new file mode 100644 index 0000000..6947e49 --- /dev/null +++ b/ironcalc_base/types/struct.CellStyleXfs.html @@ -0,0 +1,29 @@ +CellStyleXfs in ironcalc_base::types - Rust
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,
+}

Fields§

§num_fmt_id: i32§font_id: i32§fill_id: i32§border_id: i32§apply_number_format: bool§apply_border: bool§apply_alignment: bool§apply_protection: bool§apply_font: bool§apply_fill: bool

Trait Implementations§

source§

impl Clone for CellStyleXfs

source§

fn clone(&self) -> CellStyleXfs

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CellStyleXfs

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for CellStyleXfs

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for CellStyleXfs

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for CellStyleXfs

source§

fn eq(&self, other: &CellStyleXfs) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for CellStyleXfs

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for CellStyleXfs

source§

impl StructuralEq for CellStyleXfs

source§

impl StructuralPartialEq for CellStyleXfs

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.CellStyles.html b/ironcalc_base/types/struct.CellStyles.html new file mode 100644 index 0000000..b0c02b8 --- /dev/null +++ b/ironcalc_base/types/struct.CellStyles.html @@ -0,0 +1,22 @@ +CellStyles in ironcalc_base::types - Rust
pub struct CellStyles {
+    pub name: String,
+    pub xf_id: i32,
+    pub builtin_id: i32,
+}

Fields§

§name: String§xf_id: i32§builtin_id: i32

Trait Implementations§

source§

impl Clone for CellStyles

source§

fn clone(&self) -> CellStyles

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CellStyles

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for CellStyles

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for CellStyles

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for CellStyles

source§

fn eq(&self, other: &CellStyles) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for CellStyles

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for CellStyles

source§

impl StructuralEq for CellStyles

source§

impl StructuralPartialEq for CellStyles

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.CellXfs.html b/ironcalc_base/types/struct.CellXfs.html new file mode 100644 index 0000000..60944e8 --- /dev/null +++ b/ironcalc_base/types/struct.CellXfs.html @@ -0,0 +1,32 @@ +CellXfs in ironcalc_base::types - Rust
pub struct CellXfs {
Show 13 fields + 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<Alignment>, +
}

Fields§

§xf_id: i32§num_fmt_id: i32§font_id: i32§fill_id: i32§border_id: i32§apply_number_format: bool§apply_border: bool§apply_alignment: bool§apply_protection: bool§apply_font: bool§apply_fill: bool§quote_prefix: bool§alignment: Option<Alignment>

Trait Implementations§

source§

impl Clone for CellXfs

source§

fn clone(&self) -> CellXfs

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CellXfs

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for CellXfs

source§

fn default() -> CellXfs

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for CellXfs

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for CellXfs

source§

fn eq(&self, other: &CellXfs) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for CellXfs

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for CellXfs

source§

impl StructuralEq for CellXfs

source§

impl StructuralPartialEq for CellXfs

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.Col.html b/ironcalc_base/types/struct.Col.html new file mode 100644 index 0000000..27a3700 --- /dev/null +++ b/ironcalc_base/types/struct.Col.html @@ -0,0 +1,26 @@ +Col in ironcalc_base::types - Rust

Struct ironcalc_base::types::Col

source ·
pub struct Col {
+    pub min: i32,
+    pub max: i32,
+    pub width: f64,
+    pub custom_width: bool,
+    pub style: Option<i32>,
+}

Fields§

§min: i32

First column affected by this record. Settings apply to column in [min, max] range.

+
§max: i32

Last column affected by this record. Settings apply to column in [min, max] range.

+
§width: f64§custom_width: bool§style: Option<i32>

Trait Implementations§

source§

impl Clone for Col

source§

fn clone(&self) -> Col

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Col

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Col

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Col

source§

fn eq(&self, other: &Col) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Col

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl StructuralPartialEq for Col

Auto Trait Implementations§

§

impl RefUnwindSafe for Col

§

impl Send for Col

§

impl Sync for Col

§

impl Unpin for Col

§

impl UnwindSafe for Col

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.Comment.html b/ironcalc_base/types/struct.Comment.html new file mode 100644 index 0000000..55be63f --- /dev/null +++ b/ironcalc_base/types/struct.Comment.html @@ -0,0 +1,23 @@ +Comment in ironcalc_base::types - Rust
pub struct Comment {
+    pub text: String,
+    pub author_name: String,
+    pub author_id: Option<String>,
+    pub cell_ref: String,
+}

Fields§

§text: String§author_name: String§author_id: Option<String>§cell_ref: String

Trait Implementations§

source§

impl Clone for Comment

source§

fn clone(&self) -> Comment

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Comment

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Comment

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Comment

source§

fn eq(&self, other: &Comment) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Comment

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for Comment

source§

impl StructuralEq for Comment

source§

impl StructuralPartialEq for Comment

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.DefinedName.html b/ironcalc_base/types/struct.DefinedName.html new file mode 100644 index 0000000..08b013a --- /dev/null +++ b/ironcalc_base/types/struct.DefinedName.html @@ -0,0 +1,23 @@ +DefinedName in ironcalc_base::types - Rust
pub struct DefinedName {
+    pub name: String,
+    pub formula: String,
+    pub sheet_id: Option<u32>,
+}
Expand description

A defined name. The sheet_id is the sheet index in case the name is local

+

Fields§

§name: String§formula: String§sheet_id: Option<u32>

Trait Implementations§

source§

impl Clone for DefinedName

source§

fn clone(&self) -> DefinedName

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for DefinedName

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for DefinedName

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for DefinedName

source§

fn eq(&self, other: &DefinedName) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for DefinedName

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for DefinedName

source§

impl StructuralEq for DefinedName

source§

impl StructuralPartialEq for DefinedName

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.Fill.html b/ironcalc_base/types/struct.Fill.html new file mode 100644 index 0000000..de24c57 --- /dev/null +++ b/ironcalc_base/types/struct.Fill.html @@ -0,0 +1,22 @@ +Fill in ironcalc_base::types - Rust

Struct ironcalc_base::types::Fill

source ·
pub struct Fill {
+    pub pattern_type: String,
+    pub fg_color: Option<String>,
+    pub bg_color: Option<String>,
+}

Fields§

§pattern_type: String§fg_color: Option<String>§bg_color: Option<String>

Trait Implementations§

source§

impl Clone for Fill

source§

fn clone(&self) -> Fill

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Fill

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Fill

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for Fill

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Fill

source§

fn eq(&self, other: &Fill) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Fill

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for Fill

source§

impl StructuralEq for Fill

source§

impl StructuralPartialEq for Fill

Auto Trait Implementations§

§

impl RefUnwindSafe for Fill

§

impl Send for Fill

§

impl Sync for Fill

§

impl Unpin for Fill

§

impl UnwindSafe for Fill

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.Font.html b/ironcalc_base/types/struct.Font.html new file mode 100644 index 0000000..fa8a2ac --- /dev/null +++ b/ironcalc_base/types/struct.Font.html @@ -0,0 +1,28 @@ +Font in ironcalc_base::types - Rust

Struct ironcalc_base::types::Font

source ·
pub struct Font {
+    pub strike: bool,
+    pub u: bool,
+    pub b: bool,
+    pub i: bool,
+    pub sz: i32,
+    pub color: Option<String>,
+    pub name: String,
+    pub family: i32,
+    pub scheme: FontScheme,
+}

Fields§

§strike: bool§u: bool§b: bool§i: bool§sz: i32§color: Option<String>§name: String§family: i32§scheme: FontScheme

Trait Implementations§

source§

impl Clone for Font

source§

fn clone(&self) -> Font

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Font

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Font

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for Font

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Font

source§

fn eq(&self, other: &Font) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Font

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for Font

source§

impl StructuralEq for Font

source§

impl StructuralPartialEq for Font

Auto Trait Implementations§

§

impl RefUnwindSafe for Font

§

impl Send for Font

§

impl Sync for Font

§

impl Unpin for Font

§

impl UnwindSafe for Font

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.Metadata.html b/ironcalc_base/types/struct.Metadata.html new file mode 100644 index 0000000..8d874a5 --- /dev/null +++ b/ironcalc_base/types/struct.Metadata.html @@ -0,0 +1,25 @@ +Metadata in ironcalc_base::types - Rust
pub struct Metadata {
+    pub application: String,
+    pub app_version: String,
+    pub creator: String,
+    pub last_modified_by: String,
+    pub created: String,
+    pub last_modified: String,
+}

Fields§

§application: String§app_version: String§creator: String§last_modified_by: String§created: String§last_modified: String

Trait Implementations§

source§

impl Clone for Metadata

source§

fn clone(&self) -> Metadata

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Metadata

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Metadata

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Metadata

source§

fn eq(&self, other: &Metadata) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Metadata

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for Metadata

source§

impl StructuralEq for Metadata

source§

impl StructuralPartialEq for Metadata

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.NumFmt.html b/ironcalc_base/types/struct.NumFmt.html new file mode 100644 index 0000000..f9c5845 --- /dev/null +++ b/ironcalc_base/types/struct.NumFmt.html @@ -0,0 +1,21 @@ +NumFmt in ironcalc_base::types - Rust

Struct ironcalc_base::types::NumFmt

source ·
pub struct NumFmt {
+    pub num_fmt_id: i32,
+    pub format_code: String,
+}

Fields§

§num_fmt_id: i32§format_code: String

Trait Implementations§

source§

impl Clone for NumFmt

source§

fn clone(&self) -> NumFmt

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for NumFmt

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for NumFmt

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for NumFmt

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for NumFmt

source§

fn eq(&self, other: &NumFmt) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for NumFmt

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for NumFmt

source§

impl StructuralEq for NumFmt

source§

impl StructuralPartialEq for NumFmt

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.Row.html b/ironcalc_base/types/struct.Row.html new file mode 100644 index 0000000..5045304 --- /dev/null +++ b/ironcalc_base/types/struct.Row.html @@ -0,0 +1,26 @@ +Row in ironcalc_base::types - Rust

Struct ironcalc_base::types::Row

source ·
pub struct Row {
+    pub r: i32,
+    pub height: f64,
+    pub custom_format: bool,
+    pub custom_height: bool,
+    pub s: i32,
+    pub hidden: bool,
+}

Fields§

§r: i32

Row index

+
§height: f64§custom_format: bool§custom_height: bool§s: i32§hidden: bool

Trait Implementations§

source§

impl Clone for Row

source§

fn clone(&self) -> Row

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Row

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Row

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Row

source§

fn eq(&self, other: &Row) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Row

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl StructuralPartialEq for Row

Auto Trait Implementations§

§

impl RefUnwindSafe for Row

§

impl Send for Row

§

impl Sync for Row

§

impl Unpin for Row

§

impl UnwindSafe for Row

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.SheetInfo.html b/ironcalc_base/types/struct.SheetInfo.html new file mode 100644 index 0000000..2382d23 --- /dev/null +++ b/ironcalc_base/types/struct.SheetInfo.html @@ -0,0 +1,24 @@ +SheetInfo in ironcalc_base::types - Rust
pub struct SheetInfo {
+    pub name: String,
+    pub state: String,
+    pub sheet_id: u32,
+    pub color: Option<String>,
+}
Expand description

Information need to show a sheet tab in the UI +The color is serialized only if it is not Color::None

+

Fields§

§name: String§state: String§sheet_id: u32§color: Option<String>

Trait Implementations§

source§

impl Debug for SheetInfo

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for SheetInfo

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for SheetInfo

source§

fn eq(&self, other: &SheetInfo) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for SheetInfo

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl StructuralPartialEq for SheetInfo

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.Styles.html b/ironcalc_base/types/struct.Styles.html new file mode 100644 index 0000000..638a90b --- /dev/null +++ b/ironcalc_base/types/struct.Styles.html @@ -0,0 +1,36 @@ +Styles in ironcalc_base::types - Rust

Struct ironcalc_base::types::Styles

source ·
pub struct Styles {
+    pub num_fmts: Vec<NumFmt>,
+    pub fonts: Vec<Font>,
+    pub fills: Vec<Fill>,
+    pub borders: Vec<Border>,
+    pub cell_style_xfs: Vec<CellStyleXfs>,
+    pub cell_xfs: Vec<CellXfs>,
+    pub cell_styles: Vec<CellStyles>,
+}

Fields§

§num_fmts: Vec<NumFmt>§fonts: Vec<Font>§fills: Vec<Fill>§borders: Vec<Border>§cell_style_xfs: Vec<CellStyleXfs>§cell_xfs: Vec<CellXfs>§cell_styles: Vec<CellStyles>

Implementations§

source§

impl Styles

source

pub fn create_new_style(&mut self, style: &Style) -> i32

source

pub fn get_style_index(&self, style: &Style) -> Option<i32>

source

pub fn add_named_cell_style( + &mut self, + style_name: &str, + style_index: i32 +) -> Result<(), String>

Adds a named cell style from an existing index +Fails if the named style already exists or if there is not a style with that index

+
source

pub fn get_style_index_by_name(&self, style_name: &str) -> Result<i32, String>

source

pub fn create_named_style( + &mut self, + style_name: &str, + style: &Style +) -> Result<(), String>

Trait Implementations§

source§

impl Clone for Styles

source§

fn clone(&self) -> Styles

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Styles

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Styles

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for Styles

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Styles

source§

fn eq(&self, other: &Styles) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Styles

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for Styles

source§

impl StructuralEq for Styles

source§

impl StructuralPartialEq for Styles

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.Table.html b/ironcalc_base/types/struct.Table.html new file mode 100644 index 0000000..90cf3b7 --- /dev/null +++ b/ironcalc_base/types/struct.Table.html @@ -0,0 +1,31 @@ +Table in ironcalc_base::types - Rust

Struct ironcalc_base::types::Table

source ·
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<u32>,
+    pub data_dxf_id: Option<u32>,
+    pub totals_row_dxf_id: Option<u32>,
+    pub columns: Vec<TableColumn>,
+    pub style_info: TableStyleInfo,
+    pub has_filters: bool,
+}

Fields§

§name: String§display_name: String§sheet_name: String§reference: String§totals_row_count: u32§header_row_count: u32§header_row_dxf_id: Option<u32>§data_dxf_id: Option<u32>§totals_row_dxf_id: Option<u32>§columns: Vec<TableColumn>§style_info: TableStyleInfo§has_filters: bool

Trait Implementations§

source§

impl Clone for Table

source§

fn clone(&self) -> Table

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Table

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Table

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Table

source§

fn eq(&self, other: &Table) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Table

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for Table

source§

impl StructuralEq for Table

source§

impl StructuralPartialEq for Table

Auto Trait Implementations§

§

impl RefUnwindSafe for Table

§

impl Send for Table

§

impl Sync for Table

§

impl Unpin for Table

§

impl UnwindSafe for Table

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.TableColumn.html b/ironcalc_base/types/struct.TableColumn.html new file mode 100644 index 0000000..0411b73 --- /dev/null +++ b/ironcalc_base/types/struct.TableColumn.html @@ -0,0 +1,26 @@ +TableColumn in ironcalc_base::types - Rust
pub struct TableColumn {
+    pub id: u32,
+    pub name: String,
+    pub totals_row_label: Option<String>,
+    pub header_row_dxf_id: Option<u32>,
+    pub data_dxf_id: Option<u32>,
+    pub totals_row_dxf_id: Option<u32>,
+    pub totals_row_function: Option<String>,
+}

Fields§

§id: u32§name: String§totals_row_label: Option<String>§header_row_dxf_id: Option<u32>§data_dxf_id: Option<u32>§totals_row_dxf_id: Option<u32>§totals_row_function: Option<String>

Trait Implementations§

source§

impl Clone for TableColumn

source§

fn clone(&self) -> TableColumn

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for TableColumn

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for TableColumn

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for TableColumn

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for TableColumn

source§

fn eq(&self, other: &TableColumn) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for TableColumn

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for TableColumn

source§

impl StructuralEq for TableColumn

source§

impl StructuralPartialEq for TableColumn

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.TableStyleInfo.html b/ironcalc_base/types/struct.TableStyleInfo.html new file mode 100644 index 0000000..4d95254 --- /dev/null +++ b/ironcalc_base/types/struct.TableStyleInfo.html @@ -0,0 +1,24 @@ +TableStyleInfo in ironcalc_base::types - Rust
pub struct TableStyleInfo {
+    pub name: Option<String>,
+    pub show_first_column: bool,
+    pub show_last_column: bool,
+    pub show_row_stripes: bool,
+    pub show_column_stripes: bool,
+}

Fields§

§name: Option<String>§show_first_column: bool§show_last_column: bool§show_row_stripes: bool§show_column_stripes: bool

Trait Implementations§

source§

impl Clone for TableStyleInfo

source§

fn clone(&self) -> TableStyleInfo

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for TableStyleInfo

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for TableStyleInfo

source§

fn default() -> TableStyleInfo

Returns the “default value” for a type. Read more
source§

impl<'de> Deserialize<'de> for TableStyleInfo

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for TableStyleInfo

source§

fn eq(&self, other: &TableStyleInfo) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for TableStyleInfo

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for TableStyleInfo

source§

impl StructuralEq for TableStyleInfo

source§

impl StructuralPartialEq for TableStyleInfo

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.Workbook.html b/ironcalc_base/types/struct.Workbook.html new file mode 100644 index 0000000..a327b1b --- /dev/null +++ b/ironcalc_base/types/struct.Workbook.html @@ -0,0 +1,31 @@ +Workbook in ironcalc_base::types - Rust
pub struct Workbook {
+    pub shared_strings: Vec<String>,
+    pub defined_names: Vec<DefinedName>,
+    pub worksheets: Vec<Worksheet>,
+    pub styles: Styles,
+    pub name: String,
+    pub settings: WorkbookSettings,
+    pub metadata: Metadata,
+    pub tables: HashMap<String, Table>,
+}
Expand description

An internal representation of an IronCalc Workbook

+

Fields§

§shared_strings: Vec<String>§defined_names: Vec<DefinedName>§worksheets: Vec<Worksheet>§styles: Styles§name: String§settings: WorkbookSettings§metadata: Metadata§tables: HashMap<String, Table>

Implementations§

source§

impl Workbook

source

pub fn get_worksheet_names(&self) -> Vec<String>

source

pub fn get_worksheet_ids(&self) -> Vec<u32>

source

pub fn worksheet(&self, worksheet_index: u32) -> Result<&Worksheet, String>

source

pub fn worksheet_mut( + &mut self, + worksheet_index: u32 +) -> Result<&mut Worksheet, String>

source

pub fn get_worksheets_info(&self) -> Vec<SheetInfo>

Trait Implementations§

source§

impl Clone for Workbook

source§

fn clone(&self) -> Workbook

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Workbook

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Workbook

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Workbook

source§

fn eq(&self, other: &Workbook) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Workbook

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl StructuralPartialEq for Workbook

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.WorkbookSettings.html b/ironcalc_base/types/struct.WorkbookSettings.html new file mode 100644 index 0000000..771aea9 --- /dev/null +++ b/ironcalc_base/types/struct.WorkbookSettings.html @@ -0,0 +1,21 @@ +WorkbookSettings in ironcalc_base::types - Rust
pub struct WorkbookSettings {
+    pub tz: String,
+    pub locale: String,
+}

Fields§

§tz: String§locale: String

Trait Implementations§

source§

impl Clone for WorkbookSettings

source§

fn clone(&self) -> WorkbookSettings

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for WorkbookSettings

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for WorkbookSettings

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for WorkbookSettings

source§

fn eq(&self, other: &WorkbookSettings) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for WorkbookSettings

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Eq for WorkbookSettings

source§

impl StructuralEq for WorkbookSettings

source§

impl StructuralPartialEq for WorkbookSettings

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/struct.Worksheet.html b/ironcalc_base/types/struct.Worksheet.html new file mode 100644 index 0000000..ed6ba0e --- /dev/null +++ b/ironcalc_base/types/struct.Worksheet.html @@ -0,0 +1,115 @@ +Worksheet in ironcalc_base::types - Rust
pub struct Worksheet {
Show 13 fields + pub dimension: String, + pub cols: Vec<Col>, + pub rows: Vec<Row>, + pub name: String, + pub sheet_data: SheetData, + pub shared_formulas: Vec<String>, + pub sheet_id: u32, + pub state: SheetState, + pub color: Option<String>, + pub merge_cells: Vec<String>, + pub comments: Vec<Comment>, + pub frozen_rows: i32, + pub frozen_columns: i32, +
}
Expand description

Internal representation of a worksheet Excel object

+

Fields§

§dimension: String§cols: Vec<Col>§rows: Vec<Row>§name: String§sheet_data: SheetData§shared_formulas: Vec<String>§sheet_id: u32§state: SheetState§color: Option<String>§merge_cells: Vec<String>§comments: Vec<Comment>§frozen_rows: i32§frozen_columns: i32

Implementations§

source§

impl Worksheet

source

pub fn get_name(&self) -> String

source

pub fn get_sheet_id(&self) -> u32

source

pub fn set_name(&mut self, name: &str)

source

pub fn cell(&self, row: i32, column: i32) -> Option<&Cell>

source

pub fn get_style(&self, row: i32, column: i32) -> i32

source

pub fn set_style(&mut self, style_index: i32) -> Result<(), String>

source

pub fn set_column_style( + &mut self, + column: i32, + style_index: i32 +) -> Result<(), String>

source

pub fn set_row_style( + &mut self, + row: i32, + style_index: i32 +) -> Result<(), String>

source

pub fn set_cell_style(&mut self, row: i32, column: i32, style_index: i32)

source

pub fn set_cell_with_formula( + &mut self, + row: i32, + column: i32, + index: i32, + style: i32 +)

source

pub fn set_cell_with_number( + &mut self, + row: i32, + column: i32, + value: f64, + style: i32 +)

source

pub fn set_cell_with_string( + &mut self, + row: i32, + column: i32, + index: i32, + style: i32 +)

source

pub fn set_cell_with_boolean( + &mut self, + row: i32, + column: i32, + value: bool, + style: i32 +)

source

pub fn set_cell_with_error( + &mut self, + row: i32, + column: i32, + error: Error, + style: i32 +)

source

pub fn set_cell_empty(&mut self, row: i32, column: i32)

source

pub fn set_cell_empty_with_style(&mut self, row: i32, column: i32, style: i32)

source

pub fn set_frozen_rows(&mut self, frozen_rows: i32) -> Result<(), String>

source

pub fn set_frozen_columns(&mut self, frozen_columns: i32) -> Result<(), String>

source

pub fn set_row_height(&mut self, row: i32, height: f64) -> Result<(), String>

Changes the height of a row.

+
    +
  • If the row does not a have a style we add it.
  • +
  • If it has we modify the height and make sure it is applied. +Fails if column index is outside allowed range.
  • +
+
source

pub fn set_column_width( + &mut self, + column: i32, + width: f64 +) -> Result<(), String>

Changes the width of a column.

+
    +
  • If the column does not a have a width we simply add it
  • +
  • If it has, it might be part of a range and we ned to split the range. +Fails if column index is outside allowed range.
  • +
+
source

pub fn column_width(&self, column: i32) -> Result<f64, String>

Return the width of a column in pixels

+
source

pub fn column_cell_references( + &self, + column: i32 +) -> Result<Vec<CellReferenceIndex>, String>

source

pub fn row_height(&self, row: i32) -> Result<f64, String>

Returns the height of a row in pixels

+
source

pub fn row_cell_references( + &self, + row: i32 +) -> Result<Vec<CellReferenceIndex>, String>

Returns non empty cells in a row

+
source

pub fn cell_references(&self) -> Result<Vec<CellReferenceIndex>, String>

Returns non empty cells

+
source

pub fn dimension(&self) -> WorksheetDimension

Calculates dimension of the sheet. This function isn’t cheap to calculate.

+
source

pub fn is_empty_cell(&self, row: i32, column: i32) -> Result<bool, String>

Returns true if cell is completely empty. +Cell with formula that evaluates to empty string is not considered empty.

+
source

pub fn navigate_to_edge_in_direction( + &self, + row: i32, + column: i32, + direction: NavigationDirection +) -> Result<(i32, i32), String>

It provides convenient method for user navigation in the spreadsheet by jumping to edges. +Spreadsheet engines usually allow this method of navigation by using CTRL+arrows. +Behaviour summary:

+
    +
  • if starting cell is empty then find first non empty cell in given direction
  • +
  • if starting cell is not empty, and neighbour in given direction is empty, then find +first non empty cell in given direction
  • +
  • if starting cell is not empty, and neighbour in given direction is also not empty, then +find last non empty cell in given direction
  • +
+

Trait Implementations§

source§

impl Clone for Worksheet

source§

fn clone(&self) -> Worksheet

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Worksheet

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Worksheet

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Worksheet

source§

fn eq(&self, other: &Worksheet) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Worksheet

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl StructuralPartialEq for Worksheet

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for Twhere + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/ironcalc_base/types/type.SheetData.html b/ironcalc_base/types/type.SheetData.html new file mode 100644 index 0000000..4231342 --- /dev/null +++ b/ironcalc_base/types/type.SheetData.html @@ -0,0 +1,3 @@ +SheetData in ironcalc_base::types - Rust

Type Alias ironcalc_base::types::SheetData

source ·
pub type SheetData = HashMap<i32, HashMap<i32, Cell>>;
Expand description

Internal representation of Excel’s sheet_data +It is row first and because of this all of our API’s should be row first

+

Aliased Type§

struct SheetData { /* private fields */ }
\ No newline at end of file diff --git a/ironcalc_base/worksheet/enum.NavigationDirection.html b/ironcalc_base/worksheet/enum.NavigationDirection.html new file mode 100644 index 0000000..5f2c3f6 --- /dev/null +++ b/ironcalc_base/worksheet/enum.NavigationDirection.html @@ -0,0 +1,20 @@ +NavigationDirection in ironcalc_base::worksheet - Rust
pub enum NavigationDirection {
+    Left,
+    Right,
+    Up,
+    Down,
+}

Variants§

§

Left

§

Right

§

Up

§

Down

Trait Implementations§

source§

impl Clone for NavigationDirection

source§

fn clone(&self) -> NavigationDirection

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl PartialEq for NavigationDirection

source§

fn eq(&self, other: &NavigationDirection) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Copy for NavigationDirection

source§

impl Eq for NavigationDirection

source§

impl StructuralEq for NavigationDirection

source§

impl StructuralPartialEq for NavigationDirection

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/ironcalc_base/worksheet/index.html b/ironcalc_base/worksheet/index.html new file mode 100644 index 0000000..c592e57 --- /dev/null +++ b/ironcalc_base/worksheet/index.html @@ -0,0 +1 @@ +ironcalc_base::worksheet - Rust
\ No newline at end of file diff --git a/ironcalc_base/worksheet/sidebar-items.js b/ironcalc_base/worksheet/sidebar-items.js new file mode 100644 index 0000000..9842ff5 --- /dev/null +++ b/ironcalc_base/worksheet/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["NavigationDirection"],"struct":["WorksheetDimension"]}; \ No newline at end of file diff --git a/ironcalc_base/worksheet/struct.WorksheetDimension.html b/ironcalc_base/worksheet/struct.WorksheetDimension.html new file mode 100644 index 0000000..0d02317 --- /dev/null +++ b/ironcalc_base/worksheet/struct.WorksheetDimension.html @@ -0,0 +1,19 @@ +WorksheetDimension in ironcalc_base::worksheet - Rust
pub struct WorksheetDimension {
+    pub min_row: i32,
+    pub max_row: i32,
+    pub min_column: i32,
+    pub max_column: i32,
+}

Fields§

§min_row: i32§max_row: i32§min_column: i32§max_column: i32

Trait Implementations§

source§

impl Debug for WorksheetDimension

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for WorksheetDimension

source§

fn eq(&self, other: &WorksheetDimension) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Eq for WorksheetDimension

source§

impl StructuralEq for WorksheetDimension

source§

impl StructuralPartialEq for WorksheetDimension

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/search-index.js b/search-index.js new file mode 100644 index 0000000..eaf1c83 --- /dev/null +++ b/search-index.js @@ -0,0 +1,7 @@ +var searchIndex = JSON.parse('{\ +"ironcalc":{"doc":"This crate reads an xlsx file and transforms it into an …","t":"CAAAADDLLLLMFLLLLMMMFFLLLLLLMMLLNNNNNENNLLLLLLLLLLLLLLLLLLFFFFF","n":["base","compare","error","export","import","CompareError","Diff","borrow","borrow","borrow_mut","borrow_mut","column","compare","from","from","into","into","reason","row","sheet_name","test_file","test_load_and_saving","try_from","try_from","try_into","try_into","type_id","type_id","value1","value2","vzip","vzip","Comparison","Evaluation","IO","NotImplemented","Workbook","XlsxError","Xml","Zip","borrow","borrow_mut","eq","fmt","fmt","from","from","from","from","from","from","into","to_string","try_from","try_into","type_id","user_message","vzip","save_to_json","save_to_xlsx","save_xlsx_to_writer","load_from_excel","load_model_from_xlsx"],"q":[[0,"ironcalc"],[5,"ironcalc::compare"],[32,"ironcalc::error"],[58,"ironcalc::export"],[61,"ironcalc::import"],[63,"ironcalc_base::model"],[64,"alloc::vec"],[65,"core::result"],[66,"alloc::string"],[67,"std::path"],[68,"core::any"],[69,"core::fmt"],[70,"core::fmt"],[71,"core::num::error"],[72,"core::num::dec2flt"],[73,"std::io::error"],[74,"zip::result"],[75,"ironcalc_base::types"],[76,"std::io"],[77,"std::io"]],"d":["","","","","","","","","","","","","Compares two Models in the internal representation and …","Returns the argument unchanged.","Returns the argument unchanged.","Calls U::from(self).","Calls U::from(self).","","","","Tests that file in file_path produces the same results in …","Tests that file in file_path can be converted to xlsx and …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Calls U::from(self).","","","","","","","Exports an internal representation of a workbook into an …","Exports a model to an xlsx file","","Imports a file from disk into an internal representation",""],"i":[0,0,0,0,0,0,0,4,2,4,2,2,0,4,2,4,2,2,2,2,0,0,4,2,4,2,4,2,2,2,4,2,11,11,11,11,11,0,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,0,0,0,0,0],"f":[0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,[[1,1],[[5,[[3,[2]],4]]]],[-1,-1,[]],[-1,-1,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,[6,[[5,[7,8]]]],[[6,9],[[5,[7,8]]]],[-1,[[5,[-2]]],[],[]],[-1,[[5,[-2]]],[],[]],[-1,[[5,[-2]]],[],[]],[-1,[[5,[-2]]],[],[]],[-1,10,[]],[-1,10,[]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[[11,11],12],[[11,13],14],[[11,13],14],[15,11],[16,11],[17,11],[18,11],[19,11],[-1,-1,[]],[-1,-2,[],[]],[-1,8,[]],[-1,[[5,[-2]]],[],[]],[-1,[[5,[-2]]],[],[]],[-1,10,[]],[11,8],[-1,-2,[],[]],[[20,6],7],[[1,6],[[5,[7,11]]]],[[1,-1],[[5,[-1,11]]],[21,22]],[[6,6,6],[[5,[20,11]]]],[[6,6,6],[[5,[1,11]]]]],"c":[],"p":[[3,"Model",63],[3,"Diff",5],[3,"Vec",64],[3,"CompareError",5],[4,"Result",65],[15,"str"],[15,"tuple"],[3,"String",66],[3,"Path",67],[3,"TypeId",68],[4,"XlsxError",32],[15,"bool"],[3,"Formatter",69],[6,"Result",69],[4,"Error",70],[3,"ParseIntError",71],[3,"ParseFloatError",72],[3,"Error",73],[4,"ZipError",74],[3,"Workbook",75],[8,"Write",76],[8,"Seek",76]],"b":[[43,"impl-Display-for-XlsxError"],[44,"impl-Debug-for-XlsxError"],[45,"impl-From%3CError%3E-for-XlsxError"],[46,"impl-From%3CParseIntError%3E-for-XlsxError"],[47,"impl-From%3CParseFloatError%3E-for-XlsxError"],[48,"impl-From%3CError%3E-for-XlsxError"],[49,"impl-From%3CZipError%3E-for-XlsxError"]]},\ +"ironcalc_base":{"doc":"","t":"AAAAAAAAAAADDLLLLLLLLMLLLLLLLMMMMLLLLLLLLLLNENNNLLLLLLLLLLLLLLLLLAAAAANDDENLLLLLLLLLLLLLLLLLLLLLLLLLLMLLLMLLLLLLLLLLLLLLALLLDLLMLLLLFLMLMLLLLNNNNNNNENNNNNNNDNNNNNNNLLLLLLLLLLLLLLALLLLALLLLLLLLLLAMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNENNLLLLFFFFLLLLMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNENNNNNNNNNNNNNNNNNNNNEEEENNNNNNNNNNNNNNNEENNENNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLFFFLLLLLLLLFLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMDDDDDDMMLLLLLLLLLLLLLLLLLLMMMMMLLLLLLLLLLLLLMLLLLLLMMMMMMMLLLMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLMFFFFFFFFFAAAAFFDLLMMFLLMLLLLNNENNNNNNNNNNNNNNDNNNNNNNNNNNNNNNNENNNLLLLLLLLLLLLLLLLLLFLLLLLLLLLLLLLLLNDNNNNDNNDNDNNNNNNNNDEDNNNNENNLLLLLLLLLLLLLLLLMMMMMMLLLLLLLLMLLLLLLLLLLMMLMLMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLMLLLLLLLLDDDMLLLLLLMMLLLLLLLLLMMMMLLLFLLLMMMMMMLLLMLLLMLLLLLLLLLMLLLDDDDDDDMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMLLLLLLLMLLLLLLLFFMMLLLLLLLMMMMMMMMMMMLLLLLLLMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNDNENNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNDNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNENNNNNNDNENNNNNNNNNNNNNNNNNNLMMLLLLLLLLLLLMLLLLLLLLMLLLLLLLLLLLLMLLMLLLLLLLLLLLLLLLLFLLMLLLLLLLLLMMLLLLMLMMMMLLMLLLLLLLLLLLLMLLLLLLLLLLLLLLLLLLLLLMLLLLLLLLLMRRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNENNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLFFFFFFFDNNDDENENNNNNDDEDNNNDDNDNNNNNNNDNDENNENNNNNNNNNDNNDNNNDNGDENDDDDNNNNENNDDDLMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMLMLMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMLLMMLLMMMMMMMLLLLLLLLLLLLLLLMLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMLMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMLMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMLLLLLLLLLLLLMLMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMLLLLLLMMMMMMMMMLLMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMLMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNENNDLLLLLLLLLLLLLMMMMLLLLLLLLL","n":["calc_result","cell","expressions","formatter","language","locale","model","new_empty","number_format","types","worksheet","CellReference","Range","borrow","borrow","borrow_mut","borrow_mut","clone","clone","clone_into","clone_into","column","eq","fmt","fmt","from","from","into","into","left","right","row","sheet","to_owned","to_owned","try_from","try_from","try_into","try_into","type_id","type_id","vzip","vzip","Boolean","CellValue","None","Number","String","borrow","borrow_mut","deserialize","eq","fmt","from","from","from","from","from","into","serialize","to_json_str","try_from","try_into","type_id","vzip","lexer","parser","token","types","utils","A1","Lexer","LexerError","LexerMode","R1C1","advance_token","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone_into","clone_into","clone_into","eq","eq","expect","fmt","from","from","from","get_formula","get_position","into","into","into","is_a1_mode","message","new","next_token","peek_token","position","set_formula","set_lexer_mode","to_owned","to_owned","to_owned","try_from","try_from","try_from","try_into","try_into","try_into","type_id","type_id","type_id","util","vzip","vzip","vzip","MarkedToken","borrow","borrow_mut","end","eq","fmt","fmt","from","get_tokens","into","start","to_string","token","try_from","try_into","type_id","vzip","ArrayKind","BooleanKind","CompareKind","EmptyArgKind","ErrorKind","FunctionKind","InvalidFunctionKind","Node","NumberKind","OpConcatenateKind","OpPowerKind","OpProductKind","OpRangeKind","OpSumKind","ParseErrorKind","Parser","RangeKind","ReferenceKind","StringKind","UnaryKind","VariableKind","WrongRangeKind","WrongReferenceKind","borrow","borrow","borrow_mut","borrow_mut","clone","clone","clone_into","clone_into","eq","fmt","from","from","into","into","move_formula","new","parse","set_lexer_mode","set_worksheets","stringify","to_owned","to_owned","try_from","try_from","try_into","try_into","type_id","type_id","vzip","vzip","walk","absolute_column","absolute_column","absolute_column1","absolute_column1","absolute_column2","absolute_column2","absolute_row","absolute_row","absolute_row1","absolute_row1","absolute_row2","absolute_row2","args","args","column","column","column1","column1","column2","column2","formula","kind","kind","kind","kind","kind","left","left","left","left","left","left","message","name","position","right","right","right","right","right","right","right","row","row","row1","row1","row2","row2","sheet_index","sheet_index","sheet_name","sheet_name","sheet_name","sheet_name","CellHorizontal","CellVertical","Column","ColumnMove","DisplaceData","None","Row","borrow","borrow_mut","from","into","to_excel_string","to_rc_format","to_string","to_string_displaced","try_from","try_into","type_id","vzip","column","column","column","column","delta","delta","delta","delta","delta","row","row","row","sheet","sheet","sheet","sheet","sheet","Add","Addition","All","And","Bang","Boolean","CALC","CIRC","Colon","ColumnReference","Comma","Compare","DIV","Data","Divide","EOF","ERROR","Equal","Error","Error","GreaterOrEqualThan","GreaterThan","Headers","Ident","Illegal","LeftBrace","LeftBracket","LeftParenthesis","LessOrEqualThan","LessThan","Minus","Minus","NA","NAME","NIMPL","NULL","NUM","NonEqual","Number","OpCompare","OpProduct","OpSum","OpUnary","Percent","Percentage","Power","Product","REF","Range","RangeReference","Reference","RightBrace","RightBracket","RightParenthesis","SPILL","Semicolon","String","StructuredReference","TableReference","TableSpecifier","ThisRow","Times","TokenType","Totals","VALUE","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","deserialize","eq","eq","eq","eq","eq","eq","eq","eq","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from","get_error_by_english_name","get_error_by_name","index","into","into","into","into","into","into","into","into","is_english_error_string","serialize","to_localized_error_string","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_string","to_string","to_string","to_string","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","absolute_column","absolute_row","column","left","right","row","sheet","sheet","specifier","table_name","table_reference","Area","CellReference","CellReferenceIndex","CellReferenceRC","ParsedRange","ParsedReference","absolute_column","absolute_row","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone_into","clone_into","clone_into","column","column","column","column","column","deserialize","deserialize","deserialize","eq","eq","fmt","fmt","from","from","from","from","from","from","height","into","into","into","into","into","into","left","right","row","row","row","row","row","serialize","serialize","serialize","sheet","sheet","sheet","sheet","to_owned","to_owned","to_owned","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","vzip","vzip","vzip","vzip","vzip","vzip","width","column_to_number","is_valid_column","is_valid_column_number","is_valid_identifier","is_valid_row","number_to_column","parse_reference_a1","parse_reference_r1c1","quote_name","dates","format","lexer","parser","date_to_serial_number","from_excel_date","Formatted","borrow","borrow_mut","color","error","format_number","from","into","text","try_from","try_into","type_id","vzip","Color","Comma","Compare","Condition","Day","DayName","DayNameShort","DayPadded","EOF","Equal","General","Ghost","GreaterOrEqualThan","GreaterThan","ILLEGAL","LessOrEqualThan","LessThan","Lexer","Literal","Month","MonthLetter","MonthName","MonthNameShort","MonthPadded","Percent","Period","QuestionMark","Raw","Scientific","ScientificMinus","Separator","Sharp","Spacer","Text","Token","Year","YearShort","Zero","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","eq","eq","fmt","fmt","from","from","from","into","into","into","is_date","is_digit","is_likely_date_number_format","new","next_token","peek_token","try_from","try_from","try_from","try_into","try_into","try_into","type_id","type_id","type_id","vzip","vzip","vzip","Date","DatePart","Day","DayName","DayNameShort","DayPadded","Digit","Digit","Error","ErrorPart","General","GeneralPart","Ghost","Literal","Month","MonthLetter","MonthName","MonthNameShort","MonthPadded","Number","NumberPart","ParsePart","Parser","Period","Raw","Spacer","Text","TextToken","Year","YearShort","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","color","color","comma","condition","digit_count","exponent_digit_count","from","from","from","from","from","from","from","from","index","into","into","into","into","into","into","into","into","is_date","is_error","is_scientific","kind","new","number","parse","parts","percent","precision","scientific_minus","tokens","tokens","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","use_thousands","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","Booleans","Errors","Language","booleans","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","calc","circ","clone","clone","clone","clone_into","clone_into","clone_into","deserialize","deserialize","deserialize","div","error","errors","false_value","from","from","from","get_language","into","into","into","na","name","nimpl","null","num","ref_value","serialize","serialize","serialize","spill","to_owned","to_owned","to_owned","true_value","try_from","try_from","try_from","try_into","try_into","try_into","type_id","type_id","type_id","value","vzip","vzip","vzip","Currency","CurrencyFormats","Dates","DecimalFormats","Locale","NumbersProperties","NumbersSymbols","accounting","accounting_alpha_next_to_number","accounting_no_currency","approximately_sign","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","currency","currency_formats","dates","day_names","day_names_short","decimal","decimal_formats","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","exponential","from","from","from","from","from","from","from","get_locale","get_locale_fix","group","infinity","into","into","into","into","into","into","into","iso","list","minus_sign","months","months_letter","months_short","nan","numbers","per_mille","percent_sign","plus_sign","serialize","serialize","serialize","serialize","serialize","serialize","serialize","standard","standard","standard_alpha_next_to_number","standard_no_currency","superscripting_exponent","symbol","symbols","time_separator","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","vzip","vzip","vzip","vzip","vzip","vzip","vzip","Africa__Abidjan","Africa__Accra","Africa__Addis_Ababa","Africa__Algiers","Africa__Asmara","Africa__Asmera","Africa__Bamako","Africa__Bangui","Africa__Banjul","Africa__Bissau","Africa__Blantyre","Africa__Brazzaville","Africa__Bujumbura","Africa__Cairo","Africa__Casablanca","Africa__Ceuta","Africa__Conakry","Africa__Dakar","Africa__Dar_es_Salaam","Africa__Djibouti","Africa__Douala","Africa__El_Aaiun","Africa__Freetown","Africa__Gaborone","Africa__Harare","Africa__Johannesburg","Africa__Juba","Africa__Kampala","Africa__Khartoum","Africa__Kigali","Africa__Kinshasa","Africa__Lagos","Africa__Libreville","Africa__Lome","Africa__Luanda","Africa__Lubumbashi","Africa__Lusaka","Africa__Malabo","Africa__Maputo","Africa__Maseru","Africa__Mbabane","Africa__Mogadishu","Africa__Monrovia","Africa__Nairobi","Africa__Ndjamena","Africa__Niamey","Africa__Nouakchott","Africa__Ouagadougou","Africa__PortoNovo","Africa__Sao_Tome","Africa__Timbuktu","Africa__Tripoli","Africa__Tunis","Africa__Windhoek","America__Adak","America__Anchorage","America__Anguilla","America__Antigua","America__Araguaina","America__Argentina__Buenos_Aires","America__Argentina__Catamarca","America__Argentina__ComodRivadavia","America__Argentina__Cordoba","America__Argentina__Jujuy","America__Argentina__La_Rioja","America__Argentina__Mendoza","America__Argentina__Rio_Gallegos","America__Argentina__Salta","America__Argentina__San_Juan","America__Argentina__San_Luis","America__Argentina__Tucuman","America__Argentina__Ushuaia","America__Aruba","America__Asuncion","America__Atikokan","America__Atka","America__Bahia","America__Bahia_Banderas","America__Barbados","America__Belem","America__Belize","America__BlancSablon","America__Boa_Vista","America__Bogota","America__Boise","America__Buenos_Aires","America__Cambridge_Bay","America__Campo_Grande","America__Cancun","America__Caracas","America__Catamarca","America__Cayenne","America__Cayman","America__Chicago","America__Chihuahua","America__Coral_Harbour","America__Cordoba","America__Costa_Rica","America__Creston","America__Cuiaba","America__Curacao","America__Danmarkshavn","America__Dawson","America__Dawson_Creek","America__Denver","America__Detroit","America__Dominica","America__Edmonton","America__Eirunepe","America__El_Salvador","America__Ensenada","America__Fort_Nelson","America__Fort_Wayne","America__Fortaleza","America__Glace_Bay","America__Godthab","America__Goose_Bay","America__Grand_Turk","America__Grenada","America__Guadeloupe","America__Guatemala","America__Guayaquil","America__Guyana","America__Halifax","America__Havana","America__Hermosillo","America__Indiana__Indianapolis","America__Indiana__Knox","America__Indiana__Marengo","America__Indiana__Petersburg","America__Indiana__Tell_City","America__Indiana__Vevay","America__Indiana__Vincennes","America__Indiana__Winamac","America__Indianapolis","America__Inuvik","America__Iqaluit","America__Jamaica","America__Jujuy","America__Juneau","America__Kentucky__Louisville","America__Kentucky__Monticello","America__Knox_IN","America__Kralendijk","America__La_Paz","America__Lima","America__Los_Angeles","America__Louisville","America__Lower_Princes","America__Maceio","America__Managua","America__Manaus","America__Marigot","America__Martinique","America__Matamoros","America__Mazatlan","America__Mendoza","America__Menominee","America__Merida","America__Metlakatla","America__Mexico_City","America__Miquelon","America__Moncton","America__Monterrey","America__Montevideo","America__Montreal","America__Montserrat","America__Nassau","America__New_York","America__Nipigon","America__Nome","America__Noronha","America__North_Dakota__Beulah","America__North_Dakota__Center","America__North_Dakota__New_Salem","America__Nuuk","America__Ojinaga","America__Panama","America__Pangnirtung","America__Paramaribo","America__Phoenix","America__Port_of_Spain","America__PortauPrince","America__Porto_Acre","America__Porto_Velho","America__Puerto_Rico","America__Punta_Arenas","America__Rainy_River","America__Rankin_Inlet","America__Recife","America__Regina","America__Resolute","America__Rio_Branco","America__Rosario","America__Santa_Isabel","America__Santarem","America__Santiago","America__Santo_Domingo","America__Sao_Paulo","America__Scoresbysund","America__Shiprock","America__Sitka","America__St_Barthelemy","America__St_Johns","America__St_Kitts","America__St_Lucia","America__St_Thomas","America__St_Vincent","America__Swift_Current","America__Tegucigalpa","America__Thule","America__Thunder_Bay","America__Tijuana","America__Toronto","America__Tortola","America__Vancouver","America__Virgin","America__Whitehorse","America__Winnipeg","America__Yakutat","America__Yellowknife","Antarctica__Casey","Antarctica__Davis","Antarctica__DumontDUrville","Antarctica__Macquarie","Antarctica__Mawson","Antarctica__McMurdo","Antarctica__Palmer","Antarctica__Rothera","Antarctica__South_Pole","Antarctica__Syowa","Antarctica__Troll","Antarctica__Vostok","Arctic__Longyearbyen","Asia__Aden","Asia__Almaty","Asia__Amman","Asia__Anadyr","Asia__Aqtau","Asia__Aqtobe","Asia__Ashgabat","Asia__Ashkhabad","Asia__Atyrau","Asia__Baghdad","Asia__Bahrain","Asia__Baku","Asia__Bangkok","Asia__Barnaul","Asia__Beirut","Asia__Bishkek","Asia__Brunei","Asia__Calcutta","Asia__Chita","Asia__Choibalsan","Asia__Chongqing","Asia__Chungking","Asia__Colombo","Asia__Dacca","Asia__Damascus","Asia__Dhaka","Asia__Dili","Asia__Dubai","Asia__Dushanbe","Asia__Famagusta","Asia__Gaza","Asia__Harbin","Asia__Hebron","Asia__Ho_Chi_Minh","Asia__Hong_Kong","Asia__Hovd","Asia__Irkutsk","Asia__Istanbul","Asia__Jakarta","Asia__Jayapura","Asia__Jerusalem","Asia__Kabul","Asia__Kamchatka","Asia__Karachi","Asia__Kashgar","Asia__Kathmandu","Asia__Katmandu","Asia__Khandyga","Asia__Kolkata","Asia__Krasnoyarsk","Asia__Kuala_Lumpur","Asia__Kuching","Asia__Kuwait","Asia__Macao","Asia__Macau","Asia__Magadan","Asia__Makassar","Asia__Manila","Asia__Muscat","Asia__Nicosia","Asia__Novokuznetsk","Asia__Novosibirsk","Asia__Omsk","Asia__Oral","Asia__Phnom_Penh","Asia__Pontianak","Asia__Pyongyang","Asia__Qatar","Asia__Qostanay","Asia__Qyzylorda","Asia__Rangoon","Asia__Riyadh","Asia__Saigon","Asia__Sakhalin","Asia__Samarkand","Asia__Seoul","Asia__Shanghai","Asia__Singapore","Asia__Srednekolymsk","Asia__Taipei","Asia__Tashkent","Asia__Tbilisi","Asia__Tehran","Asia__Tel_Aviv","Asia__Thimbu","Asia__Thimphu","Asia__Tokyo","Asia__Tomsk","Asia__Ujung_Pandang","Asia__Ulaanbaatar","Asia__Ulan_Bator","Asia__Urumqi","Asia__UstNera","Asia__Vientiane","Asia__Vladivostok","Asia__Yakutsk","Asia__Yangon","Asia__Yekaterinburg","Asia__Yerevan","Atlantic__Azores","Atlantic__Bermuda","Atlantic__Canary","Atlantic__Cape_Verde","Atlantic__Faeroe","Atlantic__Faroe","Atlantic__Jan_Mayen","Atlantic__Madeira","Atlantic__Reykjavik","Atlantic__South_Georgia","Atlantic__St_Helena","Atlantic__Stanley","Australia__ACT","Australia__Adelaide","Australia__Brisbane","Australia__Broken_Hill","Australia__Canberra","Australia__Currie","Australia__Darwin","Australia__Eucla","Australia__Hobart","Australia__LHI","Australia__Lindeman","Australia__Lord_Howe","Australia__Melbourne","Australia__NSW","Australia__North","Australia__Perth","Australia__Queensland","Australia__South","Australia__Sydney","Australia__Tasmania","Australia__Victoria","Australia__West","Australia__Yancowinna","Brazil__Acre","Brazil__DeNoronha","Brazil__East","Brazil__West","CET","CST6CDT","Canada__Atlantic","Canada__Central","Canada__Eastern","Canada__Mountain","Canada__Newfoundland","Canada__Pacific","Canada__Saskatchewan","Canada__Yukon","CellIndex","CellReference","CellState","Chile__Continental","Chile__EasterIsland","Cuba","EET","EST","EST5EDT","Egypt","Eire","Etc__GMT","Etc__GMT0","Etc__GMTMinus0","Etc__GMTMinus1","Etc__GMTMinus10","Etc__GMTMinus11","Etc__GMTMinus12","Etc__GMTMinus13","Etc__GMTMinus14","Etc__GMTMinus2","Etc__GMTMinus3","Etc__GMTMinus4","Etc__GMTMinus5","Etc__GMTMinus6","Etc__GMTMinus7","Etc__GMTMinus8","Etc__GMTMinus9","Etc__GMTPlus0","Etc__GMTPlus1","Etc__GMTPlus10","Etc__GMTPlus11","Etc__GMTPlus12","Etc__GMTPlus2","Etc__GMTPlus3","Etc__GMTPlus4","Etc__GMTPlus5","Etc__GMTPlus6","Etc__GMTPlus7","Etc__GMTPlus8","Etc__GMTPlus9","Etc__Greenwich","Etc__UCT","Etc__UTC","Etc__Universal","Etc__Zulu","Europe__Amsterdam","Europe__Andorra","Europe__Astrakhan","Europe__Athens","Europe__Belfast","Europe__Belgrade","Europe__Berlin","Europe__Bratislava","Europe__Brussels","Europe__Bucharest","Europe__Budapest","Europe__Busingen","Europe__Chisinau","Europe__Copenhagen","Europe__Dublin","Europe__Gibraltar","Europe__Guernsey","Europe__Helsinki","Europe__Isle_of_Man","Europe__Istanbul","Europe__Jersey","Europe__Kaliningrad","Europe__Kiev","Europe__Kirov","Europe__Kyiv","Europe__Lisbon","Europe__Ljubljana","Europe__London","Europe__Luxembourg","Europe__Madrid","Europe__Malta","Europe__Mariehamn","Europe__Minsk","Europe__Monaco","Europe__Moscow","Europe__Nicosia","Europe__Oslo","Europe__Paris","Europe__Podgorica","Europe__Prague","Europe__Riga","Europe__Rome","Europe__Samara","Europe__San_Marino","Europe__Sarajevo","Europe__Saratov","Europe__Simferopol","Europe__Skopje","Europe__Sofia","Europe__Stockholm","Europe__Tallinn","Europe__Tirane","Europe__Tiraspol","Europe__Ulyanovsk","Europe__Uzhgorod","Europe__Vaduz","Europe__Vatican","Europe__Vienna","Europe__Vilnius","Europe__Volgograd","Europe__Warsaw","Europe__Zagreb","Europe__Zaporozhye","Europe__Zurich","Evaluated","Evaluating","GB","GBEire","GMT","GMT0","GMTMinus0","GMTPlus0","Greenwich","HST","Hongkong","Iceland","Indian__Antananarivo","Indian__Chagos","Indian__Christmas","Indian__Cocos","Indian__Comoro","Indian__Kerguelen","Indian__Mahe","Indian__Maldives","Indian__Mauritius","Indian__Mayotte","Indian__Reunion","InvalidDefinedNameFormula","Iran","Israel","Jamaica","Japan","Kwajalein","Libya","MET","MST","MST7MDT","Mexico__BajaNorte","Mexico__BajaSur","Mexico__General","Model","NZ","NZCHAT","Navajo","PRC","PST8PDT","Pacific__Apia","Pacific__Auckland","Pacific__Bougainville","Pacific__Chatham","Pacific__Chuuk","Pacific__Easter","Pacific__Efate","Pacific__Enderbury","Pacific__Fakaofo","Pacific__Fiji","Pacific__Funafuti","Pacific__Galapagos","Pacific__Gambier","Pacific__Guadalcanal","Pacific__Guam","Pacific__Honolulu","Pacific__Johnston","Pacific__Kanton","Pacific__Kiritimati","Pacific__Kosrae","Pacific__Kwajalein","Pacific__Majuro","Pacific__Marquesas","Pacific__Midway","Pacific__Nauru","Pacific__Niue","Pacific__Norfolk","Pacific__Noumea","Pacific__Pago_Pago","Pacific__Palau","Pacific__Pitcairn","Pacific__Pohnpei","Pacific__Ponape","Pacific__Port_Moresby","Pacific__Rarotonga","Pacific__Saipan","Pacific__Samoa","Pacific__Tahiti","Pacific__Tarawa","Pacific__Tongatapu","Pacific__Truk","Pacific__Wake","Pacific__Wallis","Pacific__Yap","ParsedDefinedName","Poland","Portugal","ROC","ROK","RangeReference","Singapore","Style","Turkey","Tz","UCT","US__Alaska","US__Aleutian","US__Arizona","US__Central","US__EastIndiana","US__Eastern","US__Hawaii","US__IndianaStarke","US__Michigan","US__Mountain","US__Pacific","US__Samoa","UTC","Universal","WET","WSU","Zulu","add_sheet","alignment","border","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cell_formula","cells","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","column","copy_cell_style","delete_cell","delete_columns","delete_rows","delete_sheet","delete_sheet_by_name","delete_sheet_by_sheet_id","deserialize","eq","evaluate","extend_copied_value","extend_to","fill","fmt","fmt","font","formatted_cell_value","forward_references","from","from","from","from","from","from_json","from_workbook","get_all_cells","get_cell_content","get_cell_style_index","get_cell_value_by_index","get_cell_value_by_ref","get_frozen_columns","get_frozen_rows","get_milliseconds_since_epoch","get_new_sheet_id","get_style_for_cell","index","insert_columns","insert_rows","insert_sheet","into","into","into","into","into","is_empty_cell","language","locale","move_cell_value_to_area","move_column_action","new_empty","new_sheet","num_fmt","parse_reference","parsed_defined_names","parsed_formulas","parser","quote_prefix","rename_sheet","rename_sheet_by_index","row","serialize","set_cell_empty","set_cell_style","set_cell_style_by_name","set_currency","set_frozen_columns","set_frozen_rows","set_sheet_color","set_sheet_column_style","set_sheet_row_style","set_sheet_style","set_user_input","shared_strings","sheet_markup","to_json_str","to_owned","to_owned","to_owned","to_owned","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","tz","update_cell_with_bool","update_cell_with_formula","update_cell_with_number","update_cell_with_text","vzip","vzip","vzip","vzip","vzip","workbook","APPLICATION","APP_VERSION","Africa__Abidjan","Africa__Accra","Africa__Addis_Ababa","Africa__Algiers","Africa__Asmara","Africa__Asmera","Africa__Bamako","Africa__Bangui","Africa__Banjul","Africa__Bissau","Africa__Blantyre","Africa__Brazzaville","Africa__Bujumbura","Africa__Cairo","Africa__Casablanca","Africa__Ceuta","Africa__Conakry","Africa__Dakar","Africa__Dar_es_Salaam","Africa__Djibouti","Africa__Douala","Africa__El_Aaiun","Africa__Freetown","Africa__Gaborone","Africa__Harare","Africa__Johannesburg","Africa__Juba","Africa__Kampala","Africa__Khartoum","Africa__Kigali","Africa__Kinshasa","Africa__Lagos","Africa__Libreville","Africa__Lome","Africa__Luanda","Africa__Lubumbashi","Africa__Lusaka","Africa__Malabo","Africa__Maputo","Africa__Maseru","Africa__Mbabane","Africa__Mogadishu","Africa__Monrovia","Africa__Nairobi","Africa__Ndjamena","Africa__Niamey","Africa__Nouakchott","Africa__Ouagadougou","Africa__PortoNovo","Africa__Sao_Tome","Africa__Timbuktu","Africa__Tripoli","Africa__Tunis","Africa__Windhoek","America__Adak","America__Anchorage","America__Anguilla","America__Antigua","America__Araguaina","America__Argentina__Buenos_Aires","America__Argentina__Catamarca","America__Argentina__ComodRivadavia","America__Argentina__Cordoba","America__Argentina__Jujuy","America__Argentina__La_Rioja","America__Argentina__Mendoza","America__Argentina__Rio_Gallegos","America__Argentina__Salta","America__Argentina__San_Juan","America__Argentina__San_Luis","America__Argentina__Tucuman","America__Argentina__Ushuaia","America__Aruba","America__Asuncion","America__Atikokan","America__Atka","America__Bahia","America__Bahia_Banderas","America__Barbados","America__Belem","America__Belize","America__BlancSablon","America__Boa_Vista","America__Bogota","America__Boise","America__Buenos_Aires","America__Cambridge_Bay","America__Campo_Grande","America__Cancun","America__Caracas","America__Catamarca","America__Cayenne","America__Cayman","America__Chicago","America__Chihuahua","America__Coral_Harbour","America__Cordoba","America__Costa_Rica","America__Creston","America__Cuiaba","America__Curacao","America__Danmarkshavn","America__Dawson","America__Dawson_Creek","America__Denver","America__Detroit","America__Dominica","America__Edmonton","America__Eirunepe","America__El_Salvador","America__Ensenada","America__Fort_Nelson","America__Fort_Wayne","America__Fortaleza","America__Glace_Bay","America__Godthab","America__Goose_Bay","America__Grand_Turk","America__Grenada","America__Guadeloupe","America__Guatemala","America__Guayaquil","America__Guyana","America__Halifax","America__Havana","America__Hermosillo","America__Indiana__Indianapolis","America__Indiana__Knox","America__Indiana__Marengo","America__Indiana__Petersburg","America__Indiana__Tell_City","America__Indiana__Vevay","America__Indiana__Vincennes","America__Indiana__Winamac","America__Indianapolis","America__Inuvik","America__Iqaluit","America__Jamaica","America__Jujuy","America__Juneau","America__Kentucky__Louisville","America__Kentucky__Monticello","America__Knox_IN","America__Kralendijk","America__La_Paz","America__Lima","America__Los_Angeles","America__Louisville","America__Lower_Princes","America__Maceio","America__Managua","America__Manaus","America__Marigot","America__Martinique","America__Matamoros","America__Mazatlan","America__Mendoza","America__Menominee","America__Merida","America__Metlakatla","America__Mexico_City","America__Miquelon","America__Moncton","America__Monterrey","America__Montevideo","America__Montreal","America__Montserrat","America__Nassau","America__New_York","America__Nipigon","America__Nome","America__Noronha","America__North_Dakota__Beulah","America__North_Dakota__Center","America__North_Dakota__New_Salem","America__Nuuk","America__Ojinaga","America__Panama","America__Pangnirtung","America__Paramaribo","America__Phoenix","America__Port_of_Spain","America__PortauPrince","America__Porto_Acre","America__Porto_Velho","America__Puerto_Rico","America__Punta_Arenas","America__Rainy_River","America__Rankin_Inlet","America__Recife","America__Regina","America__Resolute","America__Rio_Branco","America__Rosario","America__Santa_Isabel","America__Santarem","America__Santiago","America__Santo_Domingo","America__Sao_Paulo","America__Scoresbysund","America__Shiprock","America__Sitka","America__St_Barthelemy","America__St_Johns","America__St_Kitts","America__St_Lucia","America__St_Thomas","America__St_Vincent","America__Swift_Current","America__Tegucigalpa","America__Thule","America__Thunder_Bay","America__Tijuana","America__Toronto","America__Tortola","America__Vancouver","America__Virgin","America__Whitehorse","America__Winnipeg","America__Yakutat","America__Yellowknife","Antarctica__Casey","Antarctica__Davis","Antarctica__DumontDUrville","Antarctica__Macquarie","Antarctica__Mawson","Antarctica__McMurdo","Antarctica__Palmer","Antarctica__Rothera","Antarctica__South_Pole","Antarctica__Syowa","Antarctica__Troll","Antarctica__Vostok","Arctic__Longyearbyen","Asia__Aden","Asia__Almaty","Asia__Amman","Asia__Anadyr","Asia__Aqtau","Asia__Aqtobe","Asia__Ashgabat","Asia__Ashkhabad","Asia__Atyrau","Asia__Baghdad","Asia__Bahrain","Asia__Baku","Asia__Bangkok","Asia__Barnaul","Asia__Beirut","Asia__Bishkek","Asia__Brunei","Asia__Calcutta","Asia__Chita","Asia__Choibalsan","Asia__Chongqing","Asia__Chungking","Asia__Colombo","Asia__Dacca","Asia__Damascus","Asia__Dhaka","Asia__Dili","Asia__Dubai","Asia__Dushanbe","Asia__Famagusta","Asia__Gaza","Asia__Harbin","Asia__Hebron","Asia__Ho_Chi_Minh","Asia__Hong_Kong","Asia__Hovd","Asia__Irkutsk","Asia__Istanbul","Asia__Jakarta","Asia__Jayapura","Asia__Jerusalem","Asia__Kabul","Asia__Kamchatka","Asia__Karachi","Asia__Kashgar","Asia__Kathmandu","Asia__Katmandu","Asia__Khandyga","Asia__Kolkata","Asia__Krasnoyarsk","Asia__Kuala_Lumpur","Asia__Kuching","Asia__Kuwait","Asia__Macao","Asia__Macau","Asia__Magadan","Asia__Makassar","Asia__Manila","Asia__Muscat","Asia__Nicosia","Asia__Novokuznetsk","Asia__Novosibirsk","Asia__Omsk","Asia__Oral","Asia__Phnom_Penh","Asia__Pontianak","Asia__Pyongyang","Asia__Qatar","Asia__Qostanay","Asia__Qyzylorda","Asia__Rangoon","Asia__Riyadh","Asia__Saigon","Asia__Sakhalin","Asia__Samarkand","Asia__Seoul","Asia__Shanghai","Asia__Singapore","Asia__Srednekolymsk","Asia__Taipei","Asia__Tashkent","Asia__Tbilisi","Asia__Tehran","Asia__Tel_Aviv","Asia__Thimbu","Asia__Thimphu","Asia__Tokyo","Asia__Tomsk","Asia__Ujung_Pandang","Asia__Ulaanbaatar","Asia__Ulan_Bator","Asia__Urumqi","Asia__UstNera","Asia__Vientiane","Asia__Vladivostok","Asia__Yakutsk","Asia__Yangon","Asia__Yekaterinburg","Asia__Yerevan","Atlantic__Azores","Atlantic__Bermuda","Atlantic__Canary","Atlantic__Cape_Verde","Atlantic__Faeroe","Atlantic__Faroe","Atlantic__Jan_Mayen","Atlantic__Madeira","Atlantic__Reykjavik","Atlantic__South_Georgia","Atlantic__St_Helena","Atlantic__Stanley","Australia__ACT","Australia__Adelaide","Australia__Brisbane","Australia__Broken_Hill","Australia__Canberra","Australia__Currie","Australia__Darwin","Australia__Eucla","Australia__Hobart","Australia__LHI","Australia__Lindeman","Australia__Lord_Howe","Australia__Melbourne","Australia__NSW","Australia__North","Australia__Perth","Australia__Queensland","Australia__South","Australia__Sydney","Australia__Tasmania","Australia__Victoria","Australia__West","Australia__Yancowinna","Brazil__Acre","Brazil__DeNoronha","Brazil__East","Brazil__West","CET","CST6CDT","Canada__Atlantic","Canada__Central","Canada__Eastern","Canada__Mountain","Canada__Newfoundland","Canada__Pacific","Canada__Saskatchewan","Canada__Yukon","Chile__Continental","Chile__EasterIsland","Cuba","EET","EST","EST5EDT","Egypt","Eire","Etc__GMT","Etc__GMT0","Etc__GMTMinus0","Etc__GMTMinus1","Etc__GMTMinus10","Etc__GMTMinus11","Etc__GMTMinus12","Etc__GMTMinus13","Etc__GMTMinus14","Etc__GMTMinus2","Etc__GMTMinus3","Etc__GMTMinus4","Etc__GMTMinus5","Etc__GMTMinus6","Etc__GMTMinus7","Etc__GMTMinus8","Etc__GMTMinus9","Etc__GMTPlus0","Etc__GMTPlus1","Etc__GMTPlus10","Etc__GMTPlus11","Etc__GMTPlus12","Etc__GMTPlus2","Etc__GMTPlus3","Etc__GMTPlus4","Etc__GMTPlus5","Etc__GMTPlus6","Etc__GMTPlus7","Etc__GMTPlus8","Etc__GMTPlus9","Etc__Greenwich","Etc__UCT","Etc__UTC","Etc__Universal","Etc__Zulu","Europe__Amsterdam","Europe__Andorra","Europe__Astrakhan","Europe__Athens","Europe__Belfast","Europe__Belgrade","Europe__Berlin","Europe__Bratislava","Europe__Brussels","Europe__Bucharest","Europe__Budapest","Europe__Busingen","Europe__Chisinau","Europe__Copenhagen","Europe__Dublin","Europe__Gibraltar","Europe__Guernsey","Europe__Helsinki","Europe__Isle_of_Man","Europe__Istanbul","Europe__Jersey","Europe__Kaliningrad","Europe__Kiev","Europe__Kirov","Europe__Kyiv","Europe__Lisbon","Europe__Ljubljana","Europe__London","Europe__Luxembourg","Europe__Madrid","Europe__Malta","Europe__Mariehamn","Europe__Minsk","Europe__Monaco","Europe__Moscow","Europe__Nicosia","Europe__Oslo","Europe__Paris","Europe__Podgorica","Europe__Prague","Europe__Riga","Europe__Rome","Europe__Samara","Europe__San_Marino","Europe__Sarajevo","Europe__Saratov","Europe__Simferopol","Europe__Skopje","Europe__Sofia","Europe__Stockholm","Europe__Tallinn","Europe__Tirane","Europe__Tiraspol","Europe__Ulyanovsk","Europe__Uzhgorod","Europe__Vaduz","Europe__Vatican","Europe__Vienna","Europe__Vilnius","Europe__Volgograd","Europe__Warsaw","Europe__Zagreb","Europe__Zaporozhye","Europe__Zurich","GB","GBEire","GMT","GMT0","GMTMinus0","GMTPlus0","Greenwich","HST","Hongkong","IRONCALC_USER","Iceland","Indian__Antananarivo","Indian__Chagos","Indian__Christmas","Indian__Cocos","Indian__Comoro","Indian__Kerguelen","Indian__Mahe","Indian__Maldives","Indian__Mauritius","Indian__Mayotte","Indian__Reunion","Iran","Israel","Jamaica","Japan","Kwajalein","Libya","MET","MST","MST7MDT","Mexico__BajaNorte","Mexico__BajaSur","Mexico__General","NZ","NZCHAT","Navajo","PRC","PST8PDT","Pacific__Apia","Pacific__Auckland","Pacific__Bougainville","Pacific__Chatham","Pacific__Chuuk","Pacific__Easter","Pacific__Efate","Pacific__Enderbury","Pacific__Fakaofo","Pacific__Fiji","Pacific__Funafuti","Pacific__Galapagos","Pacific__Gambier","Pacific__Guadalcanal","Pacific__Guam","Pacific__Honolulu","Pacific__Johnston","Pacific__Kanton","Pacific__Kiritimati","Pacific__Kosrae","Pacific__Kwajalein","Pacific__Majuro","Pacific__Marquesas","Pacific__Midway","Pacific__Nauru","Pacific__Niue","Pacific__Norfolk","Pacific__Noumea","Pacific__Pago_Pago","Pacific__Palau","Pacific__Pitcairn","Pacific__Pohnpei","Pacific__Ponape","Pacific__Port_Moresby","Pacific__Rarotonga","Pacific__Saipan","Pacific__Samoa","Pacific__Tahiti","Pacific__Tarawa","Pacific__Tongatapu","Pacific__Truk","Pacific__Wake","Pacific__Wallis","Pacific__Yap","Poland","Portugal","ROC","ROK","Singapore","Turkey","Tz","UCT","US__Alaska","US__Aleutian","US__Arizona","US__Central","US__EastIndiana","US__Eastern","US__Hawaii","US__IndianaStarke","US__Michigan","US__Mountain","US__Pacific","US__Samoa","UTC","Universal","WET","WSU","Zulu","borrow","borrow_mut","clone","clone_into","eq","fmt","fmt","from","from_offset","from_str","hash","into","name","offset_from_local_date","offset_from_local_datetime","offset_from_utc_date","offset_from_utc_datetime","to_owned","to_string","try_from","try_into","type_id","vzip","format_number","get_default_num_fmt_id","get_new_num_fmt_index","get_num_fmt","to_excel_precision_str","to_precision","to_precision_str","Alignment","Array","BooleanCell","Border","BorderItem","BorderStyle","Bottom","Cell","CellFormula","CellFormulaBoolean","CellFormulaError","CellFormulaNumber","CellFormulaString","CellStyleXfs","CellStyles","CellType","CellXfs","Center","Center","CenterContinuous","Col","Comment","CompoundData","DefinedName","Distributed","Distributed","Dotted","Double","EmptyCell","ErrorCell","ErrorValue","Fill","Fill","Font","FontScheme","General","Hidden","HorizontalAlignment","Justify","Justify","Left","LogicalValue","Major","Medium","MediumDashDot","MediumDashDotDot","MediumDashed","Metadata","Minor","None","NumFmt","Number","NumberCell","Right","Row","SharedString","SheetData","SheetInfo","SheetState","SlantDashDot","Styles","Table","TableColumn","TableStyleInfo","Text","Thick","Thin","Top","VerticalAlignment","VeryHidden","Visible","Workbook","WorkbookSettings","Worksheet","add_named_cell_style","alignment","app_version","application","apply_alignment","apply_alignment","apply_border","apply_border","apply_fill","apply_fill","apply_font","apply_font","apply_number_format","apply_number_format","apply_protection","apply_protection","author_id","author_name","b","bg_color","border_id","border_id","borders","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","bottom","builtin_id","cell","cell_ref","cell_references","cell_style_xfs","cell_styles","cell_xfs","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","color","color","color","color","cols","column_cell_references","column_width","columns","comments","create_named_style","create_new_style","created","creator","custom_format","custom_height","custom_width","data_dxf_id","data_dxf_id","default","default","default","default","default","default","default","default","default","default","default","default","default","default","default","defined_names","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","diagonal","diagonal_down","diagonal_up","dimension","dimension","display_name","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","eq","family","fg_color","fill_id","fill_id","fills","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","font_id","font_id","fonts","format_code","formatted_value","formula","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","frozen_columns","frozen_rows","get_formula","get_name","get_sheet_id","get_style","get_style","get_style_index","get_style_index_by_name","get_text","get_type","get_worksheet_ids","get_worksheet_names","get_worksheets_info","has_filters","has_formula","header_row_count","header_row_dxf_id","header_row_dxf_id","height","hidden","horizontal","i","id","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","is_empty_cell","last_modified","last_modified_by","left","locale","max","merge_cells","metadata","min","name","name","name","name","name","name","name","name","name","navigate_to_edge_in_direction","new_boolean","new_error","new_formula","new_number","new_string","num_fmt_id","num_fmt_id","num_fmt_id","num_fmts","pattern_type","quote_prefix","r","reference","right","row_cell_references","row_height","rows","s","scheme","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","set_cell_empty","set_cell_empty_with_style","set_cell_style","set_cell_with_boolean","set_cell_with_error","set_cell_with_formula","set_cell_with_number","set_cell_with_string","set_column_style","set_column_width","set_frozen_columns","set_frozen_rows","set_name","set_row_height","set_row_style","set_style","set_style","settings","shared_formulas","shared_strings","sheet_data","sheet_id","sheet_id","sheet_id","sheet_name","show_column_stripes","show_first_column","show_last_column","show_row_stripes","state","state","strike","style","style","style_info","styles","sz","tables","text","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_string","to_string","to_string","to_string","to_string","top","totals_row_count","totals_row_dxf_id","totals_row_dxf_id","totals_row_function","totals_row_label","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","tz","u","value","vertical","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","width","worksheet","worksheet_mut","worksheets","wrap_text","xf_id","xf_id","ei","ei","f","f","f","f","f","m","o","s","s","s","s","s","s","s","s","s","s","si","v","v","v","v","v","Down","Left","NavigationDirection","Right","Up","WorksheetDimension","borrow","borrow","borrow_mut","borrow_mut","clone","clone_into","eq","eq","fmt","from","from","into","into","max_column","max_row","min_column","min_row","to_owned","try_from","try_from","try_into","try_into","type_id","type_id","vzip","vzip"],"q":[[0,"ironcalc_base"],[11,"ironcalc_base::calc_result"],[43,"ironcalc_base::cell"],[65,"ironcalc_base::expressions"],[70,"ironcalc_base::expressions::lexer"],[124,"ironcalc_base::expressions::lexer::util"],[141,"ironcalc_base::expressions::parser"],[195,"ironcalc_base::expressions::parser::Node"],[249,"ironcalc_base::expressions::parser::stringify"],[268,"ironcalc_base::expressions::parser::stringify::DisplaceData"],[285,"ironcalc_base::expressions::token"],[473,"ironcalc_base::expressions::token::TokenType"],[484,"ironcalc_base::expressions::types"],[577,"ironcalc_base::expressions::utils"],[586,"ironcalc_base::formatter"],[590,"ironcalc_base::formatter::dates"],[592,"ironcalc_base::formatter::format"],[605,"ironcalc_base::formatter::lexer"],[677,"ironcalc_base::formatter::parser"],[792,"ironcalc_base::language"],[851,"ironcalc_base::locale"],[984,"ironcalc_base::model"],[1719,"ironcalc_base::new_empty"],[2341,"ironcalc_base::number_format"],[2348,"ironcalc_base::types"],[3059,"ironcalc_base::types::Cell"],[3084,"ironcalc_base::worksheet"],[3116,"core::fmt"],[3117,"core::fmt"],[3118,"core::any"],[3119,"serde::de"],[3120,"alloc::string"],[3121,"serde::ser"],[3122,"alloc::vec"],[3123,"std::collections::hash::map"],[3124,"core::option"],[3125,"chrono::naive::date"],[3126,"core::fmt"],[3127,"chrono::offset"],[3128,"chrono::naive::datetime"],[3129,"core::ops::function"]],"d":["","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","","","","A CellValue is the representation of the cell content.","","","","","","","","","","","","","Returns the argument unchanged.","Calls U::from(self).","","","","","","","A tokenizer for spreadsheet formulas.","GRAMAR","","","","","Tokenize an input","","","","Advances position. This is used in conjunction with …","","","","","","","","","","","","","","","Returns an error if the token is not the expected one.","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the formula","Returns the position of the lexer","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Returns true if mode is A1","","Creates a new Lexer that returns the tokens of a formula.","Returns the next token","Checks the next token without advancing position See also …","","Resets the formula","Changes the lexer mode","","","","","","","","","","","","","","","","","A MarkedToken is a token together with its position on a …","","","","","","","Returns the argument unchanged.","Returns a list of marked tokens for a formula","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","List of errors Note that “#ERROR!” and “#N/IMPL!” …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","If right is None it is just a reference Column ranges like …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Converts column letter identifier to number.","","Checks if column number is in valid range.","","","If input number is outside valid range None is returned.","","","Quotes a string sheet name if it needs to NOTE: Invalid …","","","","","","","","","","","","","Returns the argument unchanged.","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Africa/Abidjan","Africa/Accra","Africa/Addis_Ababa","Africa/Algiers","Africa/Asmara","Africa/Asmera","Africa/Bamako","Africa/Bangui","Africa/Banjul","Africa/Bissau","Africa/Blantyre","Africa/Brazzaville","Africa/Bujumbura","Africa/Cairo","Africa/Casablanca","Africa/Ceuta","Africa/Conakry","Africa/Dakar","Africa/Dar_es_Salaam","Africa/Djibouti","Africa/Douala","Africa/El_Aaiun","Africa/Freetown","Africa/Gaborone","Africa/Harare","Africa/Johannesburg","Africa/Juba","Africa/Kampala","Africa/Khartoum","Africa/Kigali","Africa/Kinshasa","Africa/Lagos","Africa/Libreville","Africa/Lome","Africa/Luanda","Africa/Lubumbashi","Africa/Lusaka","Africa/Malabo","Africa/Maputo","Africa/Maseru","Africa/Mbabane","Africa/Mogadishu","Africa/Monrovia","Africa/Nairobi","Africa/Ndjamena","Africa/Niamey","Africa/Nouakchott","Africa/Ouagadougou","Africa/Porto-Novo","Africa/Sao_Tome","Africa/Timbuktu","Africa/Tripoli","Africa/Tunis","Africa/Windhoek","America/Adak","America/Anchorage","America/Anguilla","America/Antigua","America/Araguaina","America/Argentina/Buenos_Aires","America/Argentina/Catamarca","America/Argentina/ComodRivadavia","America/Argentina/Cordoba","America/Argentina/Jujuy","America/Argentina/La_Rioja","America/Argentina/Mendoza","America/Argentina/Rio_Gallegos","America/Argentina/Salta","America/Argentina/San_Juan","America/Argentina/San_Luis","America/Argentina/Tucuman","America/Argentina/Ushuaia","America/Aruba","America/Asuncion","America/Atikokan","America/Atka","America/Bahia","America/Bahia_Banderas","America/Barbados","America/Belem","America/Belize","America/Blanc-Sablon","America/Boa_Vista","America/Bogota","America/Boise","America/Buenos_Aires","America/Cambridge_Bay","America/Campo_Grande","America/Cancun","America/Caracas","America/Catamarca","America/Cayenne","America/Cayman","America/Chicago","America/Chihuahua","America/Coral_Harbour","America/Cordoba","America/Costa_Rica","America/Creston","America/Cuiaba","America/Curacao","America/Danmarkshavn","America/Dawson","America/Dawson_Creek","America/Denver","America/Detroit","America/Dominica","America/Edmonton","America/Eirunepe","America/El_Salvador","America/Ensenada","America/Fort_Nelson","America/Fort_Wayne","America/Fortaleza","America/Glace_Bay","America/Godthab","America/Goose_Bay","America/Grand_Turk","America/Grenada","America/Guadeloupe","America/Guatemala","America/Guayaquil","America/Guyana","America/Halifax","America/Havana","America/Hermosillo","America/Indiana/Indianapolis","America/Indiana/Knox","America/Indiana/Marengo","America/Indiana/Petersburg","America/Indiana/Tell_City","America/Indiana/Vevay","America/Indiana/Vincennes","America/Indiana/Winamac","America/Indianapolis","America/Inuvik","America/Iqaluit","America/Jamaica","America/Jujuy","America/Juneau","America/Kentucky/Louisville","America/Kentucky/Monticello","America/Knox_IN","America/Kralendijk","America/La_Paz","America/Lima","America/Los_Angeles","America/Louisville","America/Lower_Princes","America/Maceio","America/Managua","America/Manaus","America/Marigot","America/Martinique","America/Matamoros","America/Mazatlan","America/Mendoza","America/Menominee","America/Merida","America/Metlakatla","America/Mexico_City","America/Miquelon","America/Moncton","America/Monterrey","America/Montevideo","America/Montreal","America/Montserrat","America/Nassau","America/New_York","America/Nipigon","America/Nome","America/Noronha","America/North_Dakota/Beulah","America/North_Dakota/Center","America/North_Dakota/New_Salem","America/Nuuk","America/Ojinaga","America/Panama","America/Pangnirtung","America/Paramaribo","America/Phoenix","America/Port_of_Spain","America/Port-au-Prince","America/Porto_Acre","America/Porto_Velho","America/Puerto_Rico","America/Punta_Arenas","America/Rainy_River","America/Rankin_Inlet","America/Recife","America/Regina","America/Resolute","America/Rio_Branco","America/Rosario","America/Santa_Isabel","America/Santarem","America/Santiago","America/Santo_Domingo","America/Sao_Paulo","America/Scoresbysund","America/Shiprock","America/Sitka","America/St_Barthelemy","America/St_Johns","America/St_Kitts","America/St_Lucia","America/St_Thomas","America/St_Vincent","America/Swift_Current","America/Tegucigalpa","America/Thule","America/Thunder_Bay","America/Tijuana","America/Toronto","America/Tortola","America/Vancouver","America/Virgin","America/Whitehorse","America/Winnipeg","America/Yakutat","America/Yellowknife","Antarctica/Casey","Antarctica/Davis","Antarctica/DumontDUrville","Antarctica/Macquarie","Antarctica/Mawson","Antarctica/McMurdo","Antarctica/Palmer","Antarctica/Rothera","Antarctica/South_Pole","Antarctica/Syowa","Antarctica/Troll","Antarctica/Vostok","Arctic/Longyearbyen","Asia/Aden","Asia/Almaty","Asia/Amman","Asia/Anadyr","Asia/Aqtau","Asia/Aqtobe","Asia/Ashgabat","Asia/Ashkhabad","Asia/Atyrau","Asia/Baghdad","Asia/Bahrain","Asia/Baku","Asia/Bangkok","Asia/Barnaul","Asia/Beirut","Asia/Bishkek","Asia/Brunei","Asia/Calcutta","Asia/Chita","Asia/Choibalsan","Asia/Chongqing","Asia/Chungking","Asia/Colombo","Asia/Dacca","Asia/Damascus","Asia/Dhaka","Asia/Dili","Asia/Dubai","Asia/Dushanbe","Asia/Famagusta","Asia/Gaza","Asia/Harbin","Asia/Hebron","Asia/Ho_Chi_Minh","Asia/Hong_Kong","Asia/Hovd","Asia/Irkutsk","Asia/Istanbul","Asia/Jakarta","Asia/Jayapura","Asia/Jerusalem","Asia/Kabul","Asia/Kamchatka","Asia/Karachi","Asia/Kashgar","Asia/Kathmandu","Asia/Katmandu","Asia/Khandyga","Asia/Kolkata","Asia/Krasnoyarsk","Asia/Kuala_Lumpur","Asia/Kuching","Asia/Kuwait","Asia/Macao","Asia/Macau","Asia/Magadan","Asia/Makassar","Asia/Manila","Asia/Muscat","Asia/Nicosia","Asia/Novokuznetsk","Asia/Novosibirsk","Asia/Omsk","Asia/Oral","Asia/Phnom_Penh","Asia/Pontianak","Asia/Pyongyang","Asia/Qatar","Asia/Qostanay","Asia/Qyzylorda","Asia/Rangoon","Asia/Riyadh","Asia/Saigon","Asia/Sakhalin","Asia/Samarkand","Asia/Seoul","Asia/Shanghai","Asia/Singapore","Asia/Srednekolymsk","Asia/Taipei","Asia/Tashkent","Asia/Tbilisi","Asia/Tehran","Asia/Tel_Aviv","Asia/Thimbu","Asia/Thimphu","Asia/Tokyo","Asia/Tomsk","Asia/Ujung_Pandang","Asia/Ulaanbaatar","Asia/Ulan_Bator","Asia/Urumqi","Asia/Ust-Nera","Asia/Vientiane","Asia/Vladivostok","Asia/Yakutsk","Asia/Yangon","Asia/Yekaterinburg","Asia/Yerevan","Atlantic/Azores","Atlantic/Bermuda","Atlantic/Canary","Atlantic/Cape_Verde","Atlantic/Faeroe","Atlantic/Faroe","Atlantic/Jan_Mayen","Atlantic/Madeira","Atlantic/Reykjavik","Atlantic/South_Georgia","Atlantic/St_Helena","Atlantic/Stanley","Australia/ACT","Australia/Adelaide","Australia/Brisbane","Australia/Broken_Hill","Australia/Canberra","Australia/Currie","Australia/Darwin","Australia/Eucla","Australia/Hobart","Australia/LHI","Australia/Lindeman","Australia/Lord_Howe","Australia/Melbourne","Australia/NSW","Australia/North","Australia/Perth","Australia/Queensland","Australia/South","Australia/Sydney","Australia/Tasmania","Australia/Victoria","Australia/West","Australia/Yancowinna","Brazil/Acre","Brazil/DeNoronha","Brazil/East","Brazil/West","CET","CST6CDT","Canada/Atlantic","Canada/Central","Canada/Eastern","Canada/Mountain","Canada/Newfoundland","Canada/Pacific","Canada/Saskatchewan","Canada/Yukon","","","","Chile/Continental","Chile/EasterIsland","Cuba","EET","EST","EST5EDT","Egypt","Eire","Etc/GMT","Etc/GMT0","Etc/GMT-0","Etc/GMT-1","Etc/GMT-10","Etc/GMT-11","Etc/GMT-12","Etc/GMT-13","Etc/GMT-14","Etc/GMT-2","Etc/GMT-3","Etc/GMT-4","Etc/GMT-5","Etc/GMT-6","Etc/GMT-7","Etc/GMT-8","Etc/GMT-9","Etc/GMT+0","Etc/GMT+1","Etc/GMT+10","Etc/GMT+11","Etc/GMT+12","Etc/GMT+2","Etc/GMT+3","Etc/GMT+4","Etc/GMT+5","Etc/GMT+6","Etc/GMT+7","Etc/GMT+8","Etc/GMT+9","Etc/Greenwich","Etc/UCT","Etc/UTC","Etc/Universal","Etc/Zulu","Europe/Amsterdam","Europe/Andorra","Europe/Astrakhan","Europe/Athens","Europe/Belfast","Europe/Belgrade","Europe/Berlin","Europe/Bratislava","Europe/Brussels","Europe/Bucharest","Europe/Budapest","Europe/Busingen","Europe/Chisinau","Europe/Copenhagen","Europe/Dublin","Europe/Gibraltar","Europe/Guernsey","Europe/Helsinki","Europe/Isle_of_Man","Europe/Istanbul","Europe/Jersey","Europe/Kaliningrad","Europe/Kiev","Europe/Kirov","Europe/Kyiv","Europe/Lisbon","Europe/Ljubljana","Europe/London","Europe/Luxembourg","Europe/Madrid","Europe/Malta","Europe/Mariehamn","Europe/Minsk","Europe/Monaco","Europe/Moscow","Europe/Nicosia","Europe/Oslo","Europe/Paris","Europe/Podgorica","Europe/Prague","Europe/Riga","Europe/Rome","Europe/Samara","Europe/San_Marino","Europe/Sarajevo","Europe/Saratov","Europe/Simferopol","Europe/Skopje","Europe/Sofia","Europe/Stockholm","Europe/Tallinn","Europe/Tirane","Europe/Tiraspol","Europe/Ulyanovsk","Europe/Uzhgorod","Europe/Vaduz","Europe/Vatican","Europe/Vienna","Europe/Vilnius","Europe/Volgograd","Europe/Warsaw","Europe/Zagreb","Europe/Zaporozhye","Europe/Zurich","","","GB","GB-Eire","GMT","GMT0","GMT-0","GMT+0","Greenwich","HST","Hongkong","Iceland","Indian/Antananarivo","Indian/Chagos","Indian/Christmas","Indian/Cocos","Indian/Comoro","Indian/Kerguelen","Indian/Mahe","Indian/Maldives","Indian/Mauritius","Indian/Mayotte","Indian/Reunion","","Iran","Israel","Jamaica","Japan","Kwajalein","Libya","MET","MST","MST7MDT","Mexico/BajaNorte","Mexico/BajaSur","Mexico/General","A model includes: * A Workbook: An internal representation …","NZ","NZ-CHAT","Navajo","PRC","PST8PDT","Pacific/Apia","Pacific/Auckland","Pacific/Bougainville","Pacific/Chatham","Pacific/Chuuk","Pacific/Easter","Pacific/Efate","Pacific/Enderbury","Pacific/Fakaofo","Pacific/Fiji","Pacific/Funafuti","Pacific/Galapagos","Pacific/Gambier","Pacific/Guadalcanal","Pacific/Guam","Pacific/Honolulu","Pacific/Johnston","Pacific/Kanton","Pacific/Kiritimati","Pacific/Kosrae","Pacific/Kwajalein","Pacific/Majuro","Pacific/Marquesas","Pacific/Midway","Pacific/Nauru","Pacific/Niue","Pacific/Norfolk","Pacific/Noumea","Pacific/Pago_Pago","Pacific/Palau","Pacific/Pitcairn","Pacific/Pohnpei","Pacific/Ponape","Pacific/Port_Moresby","Pacific/Rarotonga","Pacific/Saipan","Pacific/Samoa","Pacific/Tahiti","Pacific/Tarawa","Pacific/Tongatapu","Pacific/Truk","Pacific/Wake","Pacific/Wallis","Pacific/Yap","","Poland","Portugal","ROC","ROK","","Singapore","","Turkey","TimeZones built at compile time from the tz database","UCT","US/Alaska","US/Aleutian","US/Arizona","US/Central","US/East-Indiana","US/Eastern","US/Hawaii","US/Indiana-Starke","US/Michigan","US/Mountain","US/Pacific","US/Samoa","UTC","Universal","WET","W-SU","Zulu","Adds a sheet with a specific name Fails if a worksheet …","","","","","","","","","","","","","","","","","","","","","","","","","Deletes a cell by removing it from worksheet data.","Deletes one or more columns from the model starting at the …","Deletes one or more rows from the model starting at the …","Deletes a sheet by index. Fails if:","Deletes a sheet by name. Fails if:","Deletes a sheet by sheet_id. Fails if:","","","Evaluates the model with a top-down recursive algorithm","‘Extends’ value from cell [sheet, row, column] to […","‘Extends’ the value from cell [sheet, row, column] to […","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns a model from a String representation of a workbook","","Returns a list of all cells","Returns a string with the cell content. If there is a …","","","Gets the Excel Value (Bool, Number, String) of a cell","","","","","","","Inserts one or more new columns into the model at the …","Inserts one or more new rows into the model at the …","Inserts a sheet with a particular index Fails if a …","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Returns true if cell is completely empty. Cell with …","","","moves the value in area from source to target.","Displaces cells due to a move column action from …","Creates a new workbook with one empty sheet","Adds a sheet with a automatically generated name","","Parses a reference like “Sheet1!B4” into {0, 2, 4}","","","","","Renames a sheet and updates all existing references to …","Renames a sheet and updates all existing references to …","","","Sets cell to empty. Can be used to delete value without …","","Sets the style “style_name” in cell","","","","","","","","Sets a cell parametrized by (sheet, row, column) with value","","Returns markup representation of the given sheet.","Returns a JSON string of the workbook","","","","","","","","","","","","","","","","","","","","","Updates the value of a cell with a boolean value It does …","Updates the formula of given cell It does not change the …","Updates the value of a cell with a number It does not …","Updates the value of a cell with some text It does not …","","","","","","","","","Africa/Abidjan","Africa/Accra","Africa/Addis_Ababa","Africa/Algiers","Africa/Asmara","Africa/Asmera","Africa/Bamako","Africa/Bangui","Africa/Banjul","Africa/Bissau","Africa/Blantyre","Africa/Brazzaville","Africa/Bujumbura","Africa/Cairo","Africa/Casablanca","Africa/Ceuta","Africa/Conakry","Africa/Dakar","Africa/Dar_es_Salaam","Africa/Djibouti","Africa/Douala","Africa/El_Aaiun","Africa/Freetown","Africa/Gaborone","Africa/Harare","Africa/Johannesburg","Africa/Juba","Africa/Kampala","Africa/Khartoum","Africa/Kigali","Africa/Kinshasa","Africa/Lagos","Africa/Libreville","Africa/Lome","Africa/Luanda","Africa/Lubumbashi","Africa/Lusaka","Africa/Malabo","Africa/Maputo","Africa/Maseru","Africa/Mbabane","Africa/Mogadishu","Africa/Monrovia","Africa/Nairobi","Africa/Ndjamena","Africa/Niamey","Africa/Nouakchott","Africa/Ouagadougou","Africa/Porto-Novo","Africa/Sao_Tome","Africa/Timbuktu","Africa/Tripoli","Africa/Tunis","Africa/Windhoek","America/Adak","America/Anchorage","America/Anguilla","America/Antigua","America/Araguaina","America/Argentina/Buenos_Aires","America/Argentina/Catamarca","America/Argentina/ComodRivadavia","America/Argentina/Cordoba","America/Argentina/Jujuy","America/Argentina/La_Rioja","America/Argentina/Mendoza","America/Argentina/Rio_Gallegos","America/Argentina/Salta","America/Argentina/San_Juan","America/Argentina/San_Luis","America/Argentina/Tucuman","America/Argentina/Ushuaia","America/Aruba","America/Asuncion","America/Atikokan","America/Atka","America/Bahia","America/Bahia_Banderas","America/Barbados","America/Belem","America/Belize","America/Blanc-Sablon","America/Boa_Vista","America/Bogota","America/Boise","America/Buenos_Aires","America/Cambridge_Bay","America/Campo_Grande","America/Cancun","America/Caracas","America/Catamarca","America/Cayenne","America/Cayman","America/Chicago","America/Chihuahua","America/Coral_Harbour","America/Cordoba","America/Costa_Rica","America/Creston","America/Cuiaba","America/Curacao","America/Danmarkshavn","America/Dawson","America/Dawson_Creek","America/Denver","America/Detroit","America/Dominica","America/Edmonton","America/Eirunepe","America/El_Salvador","America/Ensenada","America/Fort_Nelson","America/Fort_Wayne","America/Fortaleza","America/Glace_Bay","America/Godthab","America/Goose_Bay","America/Grand_Turk","America/Grenada","America/Guadeloupe","America/Guatemala","America/Guayaquil","America/Guyana","America/Halifax","America/Havana","America/Hermosillo","America/Indiana/Indianapolis","America/Indiana/Knox","America/Indiana/Marengo","America/Indiana/Petersburg","America/Indiana/Tell_City","America/Indiana/Vevay","America/Indiana/Vincennes","America/Indiana/Winamac","America/Indianapolis","America/Inuvik","America/Iqaluit","America/Jamaica","America/Jujuy","America/Juneau","America/Kentucky/Louisville","America/Kentucky/Monticello","America/Knox_IN","America/Kralendijk","America/La_Paz","America/Lima","America/Los_Angeles","America/Louisville","America/Lower_Princes","America/Maceio","America/Managua","America/Manaus","America/Marigot","America/Martinique","America/Matamoros","America/Mazatlan","America/Mendoza","America/Menominee","America/Merida","America/Metlakatla","America/Mexico_City","America/Miquelon","America/Moncton","America/Monterrey","America/Montevideo","America/Montreal","America/Montserrat","America/Nassau","America/New_York","America/Nipigon","America/Nome","America/Noronha","America/North_Dakota/Beulah","America/North_Dakota/Center","America/North_Dakota/New_Salem","America/Nuuk","America/Ojinaga","America/Panama","America/Pangnirtung","America/Paramaribo","America/Phoenix","America/Port_of_Spain","America/Port-au-Prince","America/Porto_Acre","America/Porto_Velho","America/Puerto_Rico","America/Punta_Arenas","America/Rainy_River","America/Rankin_Inlet","America/Recife","America/Regina","America/Resolute","America/Rio_Branco","America/Rosario","America/Santa_Isabel","America/Santarem","America/Santiago","America/Santo_Domingo","America/Sao_Paulo","America/Scoresbysund","America/Shiprock","America/Sitka","America/St_Barthelemy","America/St_Johns","America/St_Kitts","America/St_Lucia","America/St_Thomas","America/St_Vincent","America/Swift_Current","America/Tegucigalpa","America/Thule","America/Thunder_Bay","America/Tijuana","America/Toronto","America/Tortola","America/Vancouver","America/Virgin","America/Whitehorse","America/Winnipeg","America/Yakutat","America/Yellowknife","Antarctica/Casey","Antarctica/Davis","Antarctica/DumontDUrville","Antarctica/Macquarie","Antarctica/Mawson","Antarctica/McMurdo","Antarctica/Palmer","Antarctica/Rothera","Antarctica/South_Pole","Antarctica/Syowa","Antarctica/Troll","Antarctica/Vostok","Arctic/Longyearbyen","Asia/Aden","Asia/Almaty","Asia/Amman","Asia/Anadyr","Asia/Aqtau","Asia/Aqtobe","Asia/Ashgabat","Asia/Ashkhabad","Asia/Atyrau","Asia/Baghdad","Asia/Bahrain","Asia/Baku","Asia/Bangkok","Asia/Barnaul","Asia/Beirut","Asia/Bishkek","Asia/Brunei","Asia/Calcutta","Asia/Chita","Asia/Choibalsan","Asia/Chongqing","Asia/Chungking","Asia/Colombo","Asia/Dacca","Asia/Damascus","Asia/Dhaka","Asia/Dili","Asia/Dubai","Asia/Dushanbe","Asia/Famagusta","Asia/Gaza","Asia/Harbin","Asia/Hebron","Asia/Ho_Chi_Minh","Asia/Hong_Kong","Asia/Hovd","Asia/Irkutsk","Asia/Istanbul","Asia/Jakarta","Asia/Jayapura","Asia/Jerusalem","Asia/Kabul","Asia/Kamchatka","Asia/Karachi","Asia/Kashgar","Asia/Kathmandu","Asia/Katmandu","Asia/Khandyga","Asia/Kolkata","Asia/Krasnoyarsk","Asia/Kuala_Lumpur","Asia/Kuching","Asia/Kuwait","Asia/Macao","Asia/Macau","Asia/Magadan","Asia/Makassar","Asia/Manila","Asia/Muscat","Asia/Nicosia","Asia/Novokuznetsk","Asia/Novosibirsk","Asia/Omsk","Asia/Oral","Asia/Phnom_Penh","Asia/Pontianak","Asia/Pyongyang","Asia/Qatar","Asia/Qostanay","Asia/Qyzylorda","Asia/Rangoon","Asia/Riyadh","Asia/Saigon","Asia/Sakhalin","Asia/Samarkand","Asia/Seoul","Asia/Shanghai","Asia/Singapore","Asia/Srednekolymsk","Asia/Taipei","Asia/Tashkent","Asia/Tbilisi","Asia/Tehran","Asia/Tel_Aviv","Asia/Thimbu","Asia/Thimphu","Asia/Tokyo","Asia/Tomsk","Asia/Ujung_Pandang","Asia/Ulaanbaatar","Asia/Ulan_Bator","Asia/Urumqi","Asia/Ust-Nera","Asia/Vientiane","Asia/Vladivostok","Asia/Yakutsk","Asia/Yangon","Asia/Yekaterinburg","Asia/Yerevan","Atlantic/Azores","Atlantic/Bermuda","Atlantic/Canary","Atlantic/Cape_Verde","Atlantic/Faeroe","Atlantic/Faroe","Atlantic/Jan_Mayen","Atlantic/Madeira","Atlantic/Reykjavik","Atlantic/South_Georgia","Atlantic/St_Helena","Atlantic/Stanley","Australia/ACT","Australia/Adelaide","Australia/Brisbane","Australia/Broken_Hill","Australia/Canberra","Australia/Currie","Australia/Darwin","Australia/Eucla","Australia/Hobart","Australia/LHI","Australia/Lindeman","Australia/Lord_Howe","Australia/Melbourne","Australia/NSW","Australia/North","Australia/Perth","Australia/Queensland","Australia/South","Australia/Sydney","Australia/Tasmania","Australia/Victoria","Australia/West","Australia/Yancowinna","Brazil/Acre","Brazil/DeNoronha","Brazil/East","Brazil/West","CET","CST6CDT","Canada/Atlantic","Canada/Central","Canada/Eastern","Canada/Mountain","Canada/Newfoundland","Canada/Pacific","Canada/Saskatchewan","Canada/Yukon","Chile/Continental","Chile/EasterIsland","Cuba","EET","EST","EST5EDT","Egypt","Eire","Etc/GMT","Etc/GMT0","Etc/GMT-0","Etc/GMT-1","Etc/GMT-10","Etc/GMT-11","Etc/GMT-12","Etc/GMT-13","Etc/GMT-14","Etc/GMT-2","Etc/GMT-3","Etc/GMT-4","Etc/GMT-5","Etc/GMT-6","Etc/GMT-7","Etc/GMT-8","Etc/GMT-9","Etc/GMT+0","Etc/GMT+1","Etc/GMT+10","Etc/GMT+11","Etc/GMT+12","Etc/GMT+2","Etc/GMT+3","Etc/GMT+4","Etc/GMT+5","Etc/GMT+6","Etc/GMT+7","Etc/GMT+8","Etc/GMT+9","Etc/Greenwich","Etc/UCT","Etc/UTC","Etc/Universal","Etc/Zulu","Europe/Amsterdam","Europe/Andorra","Europe/Astrakhan","Europe/Athens","Europe/Belfast","Europe/Belgrade","Europe/Berlin","Europe/Bratislava","Europe/Brussels","Europe/Bucharest","Europe/Budapest","Europe/Busingen","Europe/Chisinau","Europe/Copenhagen","Europe/Dublin","Europe/Gibraltar","Europe/Guernsey","Europe/Helsinki","Europe/Isle_of_Man","Europe/Istanbul","Europe/Jersey","Europe/Kaliningrad","Europe/Kiev","Europe/Kirov","Europe/Kyiv","Europe/Lisbon","Europe/Ljubljana","Europe/London","Europe/Luxembourg","Europe/Madrid","Europe/Malta","Europe/Mariehamn","Europe/Minsk","Europe/Monaco","Europe/Moscow","Europe/Nicosia","Europe/Oslo","Europe/Paris","Europe/Podgorica","Europe/Prague","Europe/Riga","Europe/Rome","Europe/Samara","Europe/San_Marino","Europe/Sarajevo","Europe/Saratov","Europe/Simferopol","Europe/Skopje","Europe/Sofia","Europe/Stockholm","Europe/Tallinn","Europe/Tirane","Europe/Tiraspol","Europe/Ulyanovsk","Europe/Uzhgorod","Europe/Vaduz","Europe/Vatican","Europe/Vienna","Europe/Vilnius","Europe/Volgograd","Europe/Warsaw","Europe/Zagreb","Europe/Zaporozhye","Europe/Zurich","GB","GB-Eire","GMT","GMT0","GMT-0","GMT+0","Greenwich","HST","Hongkong","","Iceland","Indian/Antananarivo","Indian/Chagos","Indian/Christmas","Indian/Cocos","Indian/Comoro","Indian/Kerguelen","Indian/Mahe","Indian/Maldives","Indian/Mauritius","Indian/Mayotte","Indian/Reunion","Iran","Israel","Jamaica","Japan","Kwajalein","Libya","MET","MST","MST7MDT","Mexico/BajaNorte","Mexico/BajaSur","Mexico/General","NZ","NZ-CHAT","Navajo","PRC","PST8PDT","Pacific/Apia","Pacific/Auckland","Pacific/Bougainville","Pacific/Chatham","Pacific/Chuuk","Pacific/Easter","Pacific/Efate","Pacific/Enderbury","Pacific/Fakaofo","Pacific/Fiji","Pacific/Funafuti","Pacific/Galapagos","Pacific/Gambier","Pacific/Guadalcanal","Pacific/Guam","Pacific/Honolulu","Pacific/Johnston","Pacific/Kanton","Pacific/Kiritimati","Pacific/Kosrae","Pacific/Kwajalein","Pacific/Majuro","Pacific/Marquesas","Pacific/Midway","Pacific/Nauru","Pacific/Niue","Pacific/Norfolk","Pacific/Noumea","Pacific/Pago_Pago","Pacific/Palau","Pacific/Pitcairn","Pacific/Pohnpei","Pacific/Ponape","Pacific/Port_Moresby","Pacific/Rarotonga","Pacific/Saipan","Pacific/Samoa","Pacific/Tahiti","Pacific/Tarawa","Pacific/Tongatapu","Pacific/Truk","Pacific/Wake","Pacific/Wallis","Pacific/Yap","Poland","Portugal","ROC","ROK","Singapore","Turkey","TimeZones built at compile time from the tz database","UCT","US/Alaska","US/Aleutian","US/Arizona","US/Central","US/East-Indiana","US/Eastern","US/Hawaii","US/Indiana-Starke","US/Michigan","US/Mountain","US/Pacific","US/Samoa","UTC","Universal","WET","W-SU","Zulu","","","","","","","","Returns the argument unchanged.","","","","Calls U::from(self).","","","","","","","","","","","","","","","","It rounds a f64 with p significant figures:","","","","","","","","","","","","","","","","","","Cell type enum matching Excel TYPE() function values.","","","","","","","","A defined name. The sheet_id is the sheet index in case …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Internal representation of Excel’s sheet_data It is row …","Information need to show a sheet tab in the UI The color …","Internal representation of a worksheet Excel object","","","","","","","","","","","","","An internal representation of an IronCalc Workbook","","Internal representation of a worksheet Excel object","Adds a named cell style from an existing index Fails if …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns non empty cells","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Return the width of a column in pixels","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Calculates dimension of the sheet. This function isn’t …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","Returns the formula of a cell if any.","","","","","","","","","","","","","","","","","","","","","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Returns true if cell is completely empty. Cell with …","","","","","Last column affected by this record. Settings apply to …","","","First column affected by this record. Settings apply to …","","","","","","","","","","It provides convenient method for user navigation in the …","Creates a new Cell with a boolean","Creates a new Cell with an error value","Creates a new Cell with an unevaluated formula","Creates a new Cell with a number","Creates a new Cell with a shared string (si is the string …","","","","","","","Row index","","","Returns non empty cells in a row","Returns the height of a row in pixels","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Changes the width of a column.","","","","Changes the height of a row.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","",""],"i":[0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,1,2,1,2,1,2,1,1,1,2,1,2,1,2,2,2,1,1,1,2,1,2,1,2,1,2,1,2,9,0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,0,0,0,0,17,0,0,0,17,15,16,17,15,16,17,15,16,17,15,16,17,15,16,17,15,16,16,17,15,15,15,16,17,15,15,16,15,15,15,16,15,15,16,17,15,16,17,15,16,17,15,16,17,15,0,16,17,15,0,22,22,22,22,22,22,22,0,22,22,22,22,22,22,22,22,24,24,24,24,24,24,24,0,24,24,24,24,24,24,24,0,24,24,24,24,24,24,24,24,25,24,25,24,25,24,25,24,24,24,25,24,25,0,25,25,25,25,0,24,25,24,25,24,25,24,25,24,25,0,101,102,103,104,103,104,101,102,103,104,103,104,105,106,101,102,103,104,103,104,107,108,109,105,110,111,112,113,108,109,114,110,107,106,107,112,113,108,109,114,110,111,101,102,103,104,103,104,101,103,101,103,102,104,30,30,30,30,0,30,30,30,30,30,30,0,0,0,0,30,30,30,30,115,116,117,118,115,119,116,117,118,119,116,117,115,119,116,117,118,33,18,36,18,18,18,35,35,18,37,18,18,35,36,34,18,35,31,0,18,31,31,36,18,18,18,18,18,31,31,32,33,35,35,35,35,35,31,18,0,0,0,0,18,32,18,18,35,18,37,18,18,18,18,35,18,18,18,0,0,36,34,0,36,35,31,32,33,34,35,36,37,18,31,32,33,34,35,36,37,18,31,32,33,34,35,36,37,18,31,32,33,34,35,36,37,18,35,31,32,33,34,35,36,37,18,31,31,32,32,33,33,34,34,35,35,36,37,18,18,31,32,33,34,35,36,37,18,0,0,0,31,32,33,34,35,36,37,18,0,35,35,31,32,33,34,35,36,37,18,31,32,33,34,35,18,31,32,33,34,35,36,37,18,31,32,33,34,35,36,37,18,31,32,33,34,35,36,37,18,31,32,33,34,35,36,37,18,120,120,120,121,121,120,120,121,122,122,122,0,0,0,0,0,0,39,39,123,124,39,28,40,41,123,124,39,28,40,41,39,28,40,39,28,40,124,39,28,40,41,39,40,41,39,40,39,40,123,124,39,28,40,41,41,123,124,39,28,40,41,123,123,124,39,28,40,41,39,40,41,124,28,40,41,39,28,40,123,124,39,28,40,41,123,124,39,28,40,41,123,124,39,28,40,41,123,124,39,28,40,41,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,44,44,44,0,44,44,44,44,44,44,44,45,45,0,45,45,45,45,45,45,46,45,45,46,46,45,46,46,0,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,0,45,45,45,47,45,46,47,45,46,45,46,45,46,47,45,46,47,45,46,45,45,0,47,47,47,47,45,46,47,45,46,47,45,46,47,45,46,48,0,125,125,125,125,0,125,48,0,48,0,125,125,125,125,125,125,125,48,0,0,0,125,125,125,125,0,125,125,126,125,127,128,129,130,48,49,126,125,127,128,129,130,48,49,127,128,127,127,127,127,126,125,127,128,129,130,48,49,126,126,125,127,128,129,130,48,49,48,48,127,126,49,126,49,49,127,127,127,127,128,126,125,127,128,129,130,48,49,126,125,127,128,129,130,48,49,126,125,127,128,129,130,48,49,127,126,125,127,128,129,130,48,49,0,0,0,21,50,51,21,50,51,21,51,51,50,51,21,50,51,21,50,51,21,51,51,21,50,50,51,21,0,50,51,21,51,51,51,51,51,51,50,51,21,51,50,51,21,50,50,51,21,50,51,21,50,51,21,51,50,51,21,0,0,0,0,0,0,0,56,56,56,55,20,52,53,54,55,56,57,20,52,53,54,55,56,57,20,52,53,54,55,56,57,20,52,53,54,55,56,57,20,53,20,54,54,55,53,20,52,53,54,55,56,57,55,20,52,53,54,55,56,57,0,0,55,55,20,52,53,54,55,56,57,52,55,55,54,54,54,55,20,55,55,55,20,52,53,54,55,56,57,56,57,56,56,55,52,53,55,20,52,53,54,55,56,57,20,52,53,54,55,56,57,20,52,53,54,55,56,57,20,52,53,54,55,56,57,20,52,53,54,55,56,57,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,60,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,59,59,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,60,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,64,64,60,64,0,64,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,58,61,61,63,59,60,58,61,63,59,60,58,61,58,58,59,60,58,61,59,60,58,61,63,58,58,58,58,58,58,58,61,61,58,58,58,61,60,61,61,58,58,63,59,60,58,61,58,58,58,58,58,58,58,58,58,0,58,58,63,58,58,58,63,59,60,58,61,58,58,58,58,58,58,58,61,58,58,58,58,61,58,58,63,61,58,58,58,58,58,58,58,58,58,58,58,58,58,58,59,60,58,61,63,59,60,58,61,63,59,60,58,61,63,59,60,58,61,58,58,58,58,58,63,59,60,58,61,58,0,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,0,0,0,0,98,74,0,0,0,88,0,74,74,74,74,74,0,0,0,0,87,88,87,0,0,98,0,87,88,93,93,74,74,98,0,87,0,0,87,78,0,87,88,87,98,84,93,93,93,93,0,84,84,0,98,74,87,0,74,0,0,0,93,0,0,0,0,98,93,93,88,0,78,78,0,0,0,72,91,75,75,90,91,90,91,90,91,90,91,90,91,90,91,81,81,85,86,90,91,72,75,76,62,77,78,73,79,80,98,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,96,75,76,62,77,78,73,79,80,98,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,96,95,92,73,81,73,72,72,72,75,76,62,77,78,73,79,80,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,75,76,62,77,78,73,79,80,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,73,85,94,96,73,73,73,26,73,72,72,75,75,79,79,80,26,82,74,82,83,72,69,84,85,86,87,88,89,90,91,92,95,62,75,76,62,77,78,73,79,80,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,96,95,95,95,73,73,26,75,76,62,77,78,73,79,80,98,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,96,85,86,90,91,72,75,76,62,77,78,78,73,79,80,98,74,81,26,82,83,72,69,84,84,85,86,87,87,88,88,89,90,91,92,93,93,94,95,96,90,91,72,69,74,77,75,76,62,77,78,73,79,80,98,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,96,73,73,74,73,73,73,74,72,72,74,74,62,62,62,26,74,26,26,82,79,79,89,85,82,75,76,62,77,78,73,79,80,98,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,96,73,75,75,95,76,80,73,62,80,62,77,73,26,82,83,85,92,96,73,74,74,74,74,74,69,90,91,72,86,91,79,26,95,73,73,73,79,85,75,76,62,77,78,73,79,80,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,96,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,74,62,73,62,73,77,73,96,26,83,83,83,83,73,96,85,80,94,26,62,85,62,81,75,76,62,77,78,73,79,80,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,78,84,87,88,93,95,26,26,82,82,82,75,76,62,77,78,73,79,80,98,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,96,75,76,62,77,78,73,79,80,98,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,96,75,76,62,77,78,73,79,80,98,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,96,76,85,74,89,75,76,62,77,78,73,79,80,98,74,81,26,82,83,72,69,84,85,86,87,88,89,90,91,92,93,94,95,96,80,62,62,62,89,91,92,131,132,133,134,135,136,132,132,132,137,138,139,131,140,133,134,135,136,132,140,138,139,134,135,136,100,100,0,100,100,0,97,100,97,100,100,100,97,100,97,97,100,97,100,97,97,97,97,100,97,100,97,100,97,100,97,100],"f":[0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[1,1],[2,2],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],0,[[1,1],4],[[1,5],6],[[2,5],6],[-1,-1,[]],[-1,-1,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,8,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[7,[9]]],10],[[9,9],4],[[9,5],6],[11,9],[12,9],[13,9],[4,9],[-1,-1,[]],[-1,-2,[],[]],[[9,-1],7,14],[9,11],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,[15,3],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[16,16],[17,17],[15,15],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[16,16],4],[[17,17],4],[[15,18],[[7,[3,16]]]],[[16,5],6],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[15,11],[15,19],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[15,4],0,[[12,17,20,21],15],[15,18],[15,18],0,[[15,12],3],[[15,17],3],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,[-1,-2,[],[]],[-1,-2,[],[]],0,[[22,22],4],[[22,5],6],[[22,5],6],[-1,-1,[]],[12,[[23,[22]]]],[-1,-2,[],[]],0,[-1,11,[]],0,[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[24,24],[25,25],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[24,24],4],[[24,5],6],[-1,-1,[]],[-1,-1,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,[[[23,[11]],[27,[11,26]]],25],[[25,12,[29,[28]]],24],[[25,17],3],[[25,[23,[11]]],3],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,8,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-1,[]],[-1,-2,[],[]],[[24,28],11],[24,11],[[24,28],11],[[24,28,30],11],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[31,31],[32,32],[33,33],[34,34],[35,35],[36,36],[37,37],[18,18],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[-1,[[7,[35]]],10],[[31,31],4],[[32,32],4],[[33,33],4],[[34,34],4],[[35,35],4],[[36,36],4],[[37,37],4],[[18,18],4],[[31,5],6],[[31,5],6],[[32,5],6],[[32,5],6],[[33,5],6],[[33,5],6],[[34,5],6],[[34,5],6],[[35,5],6],[[35,5],6],[[36,5],6],[[37,5],6],[[18,5],6],[[18,5],6],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[12,[[29,[35]]]],[[12,21],[[29,[35]]]],[18,38],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[12,4],[[35,-1],7,14],[[35,21],11],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,11,[]],[-1,11,[]],[-1,11,[]],[-1,11,[]],[-1,11,[]],[-1,11,[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[39,39],[28,28],[40,40],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],0,0,0,0,0,[-1,[[7,[39]]],10],[-1,[[7,[40]]],10],[-1,[[7,[41]]],10],[[39,39],4],[[40,40],4],[[39,5],6],[[40,5],6],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,[[39,-1],7,14],[[40,-1],7,14],[[41,-1],7,14],0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,[12,[[7,[19,11]]]],[12,4],[19,4],[12,4],[19,4],[19,[[29,[11]]]],[12,[[29,[39]]]],[12,[[29,[39]]]],[12,11],0,0,0,0,[[38,38,19],[[7,[19,11]]]],[42,43],0,[-1,-2,[],[]],[-1,-2,[],[]],0,0,[[13,12,20],44],[-1,-1,[]],[-1,-2,[],[]],0,[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[45,45],4],[[46,46],4],[[45,5],6],[[46,5],6],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[45,4],[45,4],[12,4],[12,47],[47,45],[47,45],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[48,4],[48,4],0,0,[12,49],0,[49,3],0,0,0,0,0,0,[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,[50,50],[51,51],[21,21],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[-1,[[7,[50]]],10],[-1,[[7,[51]]],10],[-1,[[7,[21]]],10],0,0,0,0,[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[12,[[7,[21,11]]]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,[[50,-1],7,14],[[51,-1],7,14],[[21,-1],7,14],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[20,20],[52,52],[53,53],[54,54],[55,55],[56,56],[57,57],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],0,0,0,0,0,0,0,[-1,[[7,[20]]],10],[-1,[[7,[52]]],10],[-1,[[7,[53]]],10],[-1,[[7,[54]]],10],[-1,[[7,[55]]],10],[-1,[[7,[56]]],10],[-1,[[7,[57]]],10],0,[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[12,[[7,[20,11]]]],[12,[[7,[20,11]]]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,[[20,-1],7,14],[[52,-1],7,14],[[53,-1],7,14],[[54,-1],7,14],[[55,-1],7,14],[[56,-1],7,14],[[57,-1],7,14],0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[[58,12],[[7,[3,11]]]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[58,38,19,19],[[7,[[29,[11]],11]]]],0,[59,59],[60,60],[58,58],[61,61],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],0,[[58,[3,[38,19,19]],[3,[38,19,19]]],[[7,[3,11]]]],[[58,38,19,19],[[7,[3,11]]]],[[58,38,19,19],[[7,[3,11]]]],[[58,38,19,19],[[7,[3,11]]]],[[58,38],[[7,[3,11]]]],[[58,12],[[7,[3,11]]]],[[58,38],[[7,[3,11]]]],[-1,[[7,[61]]],10],[[61,61],4],[58,3],[[58,12,12,40,40],[[7,[11,11]]]],[[58,38,19,19,19,19],[[7,[11,11]]]],0,[[60,5],6],[[61,5],6],0,[[58,38,19,19],[[7,[11,11]]]],[[58,41,40],[[7,[[23,[0]],11]]]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[12,[[7,[58,11]]]],[62,[[7,[58,11]]]],[58,[[23,[63]]]],[[58,38,19,19],[[7,[11,11]]]],[[58,38,19,19],19],[[58,38,19,19],[[7,[9,11]]]],[[58,12],[[7,[9,11]]]],[[58,38],[[7,[19,11]]]],[[58,38],[[7,[19,11]]]],[[],42],[58,38],[[58,38,19,19],61],0,[[58,38,19,19],[[7,[3,11]]]],[[58,38,19,19],[[7,[3,11]]]],[[58,12,38,[29,[38]]],[[7,[3,11]]]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[58,38,19,19],[[7,[4,11]]]],0,0,[[58,12,40,40,41],[[7,[11,11]]]],[[58,38,19,19],[[7,[3,12]]]],[[12,12,12],[[7,[58,11]]]],[58,3],0,[[58,12],[[29,[1]]]],0,0,0,0,[[58,12,12],[[7,[3,11]]]],[[58,38,12],[[7,[3,11]]]],0,[[61,-1],7,14],[[58,38,19,19],[[7,[3,11]]]],[[58,38,19,19,61],[[7,[3,11]]]],[[58,38,19,19,12],[[7,[3,11]]]],[[58,12],[[7,[3,12]]]],[[58,38,19],[[7,[3,11]]]],[[58,38,19],[[7,[3,11]]]],[[58,38,12],[[7,[3,11]]]],[[58,38,19,12],[[7,[3,11]]]],[[58,38,19,12],[[7,[3,11]]]],[[58,38,12],[[7,[3,11]]]],[[58,38,19,19,11],3],0,[[58,38],[[7,[11,11]]]],[58,11],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],0,[[58,38,19,19,4],3],[[58,38,19,19,11],[[7,[3,11]]]],[[58,38,19,19,13],3],[[58,38,19,19,12],3],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[64,64],[[-1,-2],3,[],[]],[[64,64],4],[[64,5],[[7,[3,65]]]],[[64,5],[[7,[3,65]]]],[-1,-1,[]],[[],64],[12,[[7,[64]]]],[[64,-1],3,66],[-1,-2,[],[]],[64,12],[[64,43],67],[[64,68],67],[[64,43]],[[64,68]],[-1,-2,[],[]],[-1,11,[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,-2,[],[]],[[13,12,12],44],[12,[[29,[19]]]],[[[70,[69]]],19],[[19,[70,[69]]],11],[13,11],[[13,71],13],[[13,71],11],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[[72,12,19],[[7,[3,11]]]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,[[73,19,19],[[29,[74]]]],0,[73,[[7,[[23,[40]],11]]]],0,0,0,[75,75],[76,76],[62,62],[77,77],[78,78],[73,73],[79,79],[80,80],[74,74],[81,81],[26,26],[82,82],[83,83],[72,72],[69,69],[84,84],[85,85],[86,86],[87,87],[88,88],[89,89],[90,90],[91,91],[92,92],[93,93],[94,94],[95,95],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],[[-1,-2],3,[],[]],0,0,0,0,0,[[73,19],[[7,[[23,[40]],11]]]],[[73,19],[[7,[13,11]]]],0,0,[[72,12,61],[[7,[3,11]]]],[[72,61],19],0,0,0,0,0,0,0,[[],74],[[],82],[[],83],[[],72],[[],69],[[],84],[[],85],[[],86],[[],87],[[],88],[[],89],[[],90],[[],91],[[],92],[[],95],0,[-1,[[7,[75]]],10],[-1,[[7,[76]]],10],[-1,[[7,[62]]],10],[-1,[[7,[77]]],10],[-1,[[7,[78]]],10],[-1,[[7,[73]]],10],[-1,[[7,[79]]],10],[-1,[[7,[80]]],10],[-1,[[7,[74]]],10],[-1,[[7,[81]]],10],[-1,[[7,[26]]],10],[-1,[[7,[82]]],10],[-1,[[7,[83]]],10],[-1,[[7,[72]]],10],[-1,[[7,[69]]],10],[-1,[[7,[84]]],10],[-1,[[7,[85]]],10],[-1,[[7,[86]]],10],[-1,[[7,[87]]],10],[-1,[[7,[88]]],10],[-1,[[7,[89]]],10],[-1,[[7,[90]]],10],[-1,[[7,[91]]],10],[-1,[[7,[92]]],10],[-1,[[7,[93]]],10],[-1,[[7,[94]]],10],[-1,[[7,[95]]],10],[-1,[[7,[96]]],10],0,0,0,[73,97],0,0,[[75,75],4],[[76,76],4],[[62,62],4],[[77,77],4],[[78,78],4],[[73,73],4],[[79,79],4],[[80,80],4],[[98,98],4],[[74,74],4],[[81,81],4],[[26,26],4],[[82,82],4],[[83,83],4],[[72,72],4],[[69,69],4],[[84,84],4],[[85,85],4],[[86,86],4],[[87,87],4],[[88,88],4],[[89,89],4],[[90,90],4],[[91,91],4],[[92,92],4],[[93,93],4],[[94,94],4],[[95,95],4],[[96,96],4],0,0,0,0,0,[[75,5],6],[[76,5],6],[[62,5],6],[[77,5],6],[[78,5],6],[[78,5],6],[[73,5],6],[[79,5],6],[[80,5],6],[[98,5],6],[[74,5],6],[[81,5],6],[[26,5],6],[[82,5],6],[[83,5],6],[[72,5],6],[[69,5],6],[[84,5],6],[[84,5],6],[[85,5],6],[[86,5],6],[[87,5],6],[[87,5],6],[[88,5],6],[[88,5],6],[[89,5],6],[[90,5],6],[[91,5],6],[[92,5],6],[[93,5],6],[[93,5],6],[[94,5],6],[[95,5],6],[[96,5],6],0,0,0,0,[[74,[70,[11]],21,-1],11,99],0,[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],0,0,[74,[[29,[19]]]],[73,11],[73,38],[[73,19,19],19],[74,19],[[72,61],[[29,[19]]]],[[72,12],[[7,[19,11]]]],[[74,[70,[11]],21],11],[74,98],[62,[[23,[38]]]],[62,[[23,[11]]]],[62,[[23,[96]]]],0,[74,4],0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[73,19,19],[[7,[4,11]]]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[[73,19,19,100],[[7,[[3,[19,19]],11]]]],[[4,19],74],[[35,19],74],[[19,19],74],[[13,19],74],[[19,19],74],0,0,0,0,0,0,0,0,0,[[73,19],[[7,[[23,[40]],11]]]],[[73,19],[[7,[13,11]]]],0,0,0,[[75,-1],7,14],[[76,-1],7,14],[[62,-1],7,14],[[77,-1],7,14],[[78,-1],7,14],[[73,-1],7,14],[[79,-1],7,14],[[80,-1],7,14],[[74,-1],7,14],[[81,-1],7,14],[[26,-1],7,14],[[82,-1],7,14],[[83,-1],7,14],[[72,-1],7,14],[[69,-1],7,14],[[84,-1],7,14],[[85,-1],7,14],[[86,-1],7,14],[[87,-1],7,14],[[88,-1],7,14],[[89,-1],7,14],[[90,-1],7,14],[[91,-1],7,14],[[92,-1],7,14],[[93,-1],7,14],[[94,-1],7,14],[[95,-1],7,14],[[96,-1],7,14],[[73,19,19],3],[[73,19,19,19],3],[[73,19,19,19],3],[[73,19,19,4,19],3],[[73,19,19,35,19],3],[[73,19,19,19,19],3],[[73,19,19,13,19],3],[[73,19,19,19,19],3],[[73,19,19],[[7,[3,11]]]],[[73,19,13],[[7,[3,11]]]],[[73,19],[[7,[3,11]]]],[[73,19],[[7,[3,11]]]],[[73,12],3],[[73,19,13],[[7,[3,11]]]],[[73,19,19],[[7,[3,11]]]],[[73,19],[[7,[3,11]]]],[[74,19],3],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,11,[]],[-1,11,[]],[-1,11,[]],[-1,11,[]],[-1,11,[]],0,0,0,0,0,0,[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],[-1,8,[]],0,0,[[74,[70,[11]],21],9],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,[[62,38],[[7,[73,11]]]],[[62,38],[[7,[73,11]]]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[100,100],[[-1,-2],3,[],[]],[[97,97],4],[[100,100],4],[[97,5],6],[-1,-1,[]],[-1,-1,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,[-1,-2,[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,[[7,[-2]]],[],[]],[-1,8,[]],[-1,8,[]],[-1,-2,[],[]],[-1,-2,[],[]]],"c":[],"p":[[3,"CellReference",11],[3,"Range",11],[15,"tuple"],[15,"bool"],[3,"Formatter",3116],[6,"Result",3116],[4,"Result",3117],[3,"TypeId",3118],[4,"CellValue",43],[8,"Deserializer",3119],[3,"String",3120],[15,"str"],[15,"f64"],[8,"Serializer",3121],[3,"Lexer",70],[3,"LexerError",70],[4,"LexerMode",70],[4,"TokenType",285],[15,"i32"],[3,"Locale",851],[3,"Language",792],[3,"MarkedToken",124],[3,"Vec",3122],[4,"Node",141],[3,"Parser",141],[3,"Table",2348],[3,"HashMap",3123],[3,"CellReferenceRC",484],[4,"Option",3124],[4,"DisplaceData",249],[4,"OpCompare",285],[4,"OpUnary",285],[4,"OpSum",285],[4,"OpProduct",285],[4,"Error",285],[4,"TableSpecifier",285],[4,"TableReference",285],[15,"u32"],[3,"ParsedReference",484],[3,"CellReferenceIndex",484],[3,"Area",484],[15,"i64"],[3,"NaiveDate",3125],[3,"Formatted",592],[4,"Token",605],[4,"Compare",605],[3,"Lexer",605],[4,"ParsePart",677],[3,"Parser",677],[3,"Booleans",792],[3,"Errors",792],[3,"Currency",851],[3,"NumbersProperties",851],[3,"Dates",851],[3,"NumbersSymbols",851],[3,"CurrencyFormats",851],[3,"DecimalFormats",851],[3,"Model",984],[4,"CellState",984],[4,"ParsedDefinedName",984],[3,"Style",984],[3,"Workbook",2348],[3,"CellIndex",984],[4,"Tz",1719],[3,"Error",3116],[8,"Hasher",3126],[4,"LocalResult",3127],[3,"NaiveDateTime",3128],[3,"NumFmt",2348],[15,"slice"],[15,"usize"],[3,"Styles",2348],[3,"Worksheet",2348],[4,"Cell",2348],[3,"Metadata",2348],[3,"WorkbookSettings",2348],[3,"DefinedName",2348],[4,"SheetState",2348],[3,"Row",2348],[3,"Col",2348],[3,"Comment",2348],[3,"TableColumn",2348],[3,"TableStyleInfo",2348],[4,"FontScheme",2348],[3,"Font",2348],[3,"Fill",2348],[4,"HorizontalAlignment",2348],[4,"VerticalAlignment",2348],[3,"Alignment",2348],[3,"CellStyleXfs",2348],[3,"CellXfs",2348],[3,"CellStyles",2348],[4,"BorderStyle",2348],[3,"BorderItem",2348],[3,"Border",2348],[3,"SheetInfo",2348],[3,"WorksheetDimension",3084],[4,"CellType",2348],[8,"Fn",3129],[4,"NavigationDirection",3084],[13,"ReferenceKind",195],[13,"WrongReferenceKind",195],[13,"RangeKind",195],[13,"WrongRangeKind",195],[13,"FunctionKind",195],[13,"InvalidFunctionKind",195],[13,"ParseErrorKind",195],[13,"OpSumKind",195],[13,"OpProductKind",195],[13,"CompareKind",195],[13,"UnaryKind",195],[13,"OpRangeKind",195],[13,"OpConcatenateKind",195],[13,"OpPowerKind",195],[13,"Column",268],[13,"CellHorizontal",268],[13,"CellVertical",268],[13,"ColumnMove",268],[13,"Row",268],[13,"Reference",473],[13,"Range",473],[13,"StructuredReference",473],[3,"ParsedRange",484],[3,"CellReference",484],[4,"TextToken",677],[3,"Digit",677],[3,"NumberPart",677],[3,"DatePart",677],[3,"ErrorPart",677],[3,"GeneralPart",677],[13,"ErrorCell",3059],[13,"CellFormulaError",3059],[13,"CellFormula",3059],[13,"CellFormulaBoolean",3059],[13,"CellFormulaNumber",3059],[13,"CellFormulaString",3059],[13,"EmptyCell",3059],[13,"BooleanCell",3059],[13,"NumberCell",3059],[13,"SharedString",3059]],"b":[[53,"impl-From%3CString%3E-for-CellValue"],[54,"impl-From%3C%26str%3E-for-CellValue"],[55,"impl-From%3Cf64%3E-for-CellValue"],[56,"impl-From%3Cbool%3E-for-CellValue"],[129,"impl-Display-for-MarkedToken"],[130,"impl-Debug-for-MarkedToken"],[391,"impl-Debug-for-OpCompare"],[392,"impl-Display-for-OpCompare"],[393,"impl-Display-for-OpUnary"],[394,"impl-Debug-for-OpUnary"],[395,"impl-Debug-for-OpSum"],[396,"impl-Display-for-OpSum"],[397,"impl-Display-for-OpProduct"],[398,"impl-Debug-for-OpProduct"],[399,"impl-Debug-for-Error"],[400,"impl-Display-for-Error"],[403,"impl-Display-for-TokenType"],[404,"impl-Debug-for-TokenType"],[2323,"impl-Display-for-Tz"],[2324,"impl-Debug-for-Tz"],[2671,"impl-Display-for-SheetState"],[2672,"impl-Debug-for-SheetState"],[2684,"impl-Debug-for-FontScheme"],[2685,"impl-Display-for-FontScheme"],[2688,"impl-Display-for-HorizontalAlignment"],[2689,"impl-Debug-for-HorizontalAlignment"],[2690,"impl-Debug-for-VerticalAlignment"],[2691,"impl-Display-for-VerticalAlignment"],[2696,"impl-Debug-for-BorderStyle"],[2697,"impl-Display-for-BorderStyle"]]},\ +"test":{"doc":"Tests an Excel xlsx file. Returns a list of differences in …","t":"F","n":["main"],"q":[[0,"test"]],"d":[""],"i":[0],"f":[[[],1]],"c":[],"p":[[15,"tuple"]],"b":[]}\ +}'); +if (typeof window !== 'undefined' && window.initSearch) {window.initSearch(searchIndex)}; +if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; diff --git a/settings.html b/settings.html new file mode 100644 index 0000000..5ec04be --- /dev/null +++ b/settings.html @@ -0,0 +1 @@ +Settings

Rustdoc settings

Back
\ No newline at end of file diff --git a/src-files.js b/src-files.js new file mode 100644 index 0000000..9ba9e7f --- /dev/null +++ b/src-files.js @@ -0,0 +1,6 @@ +var srcIndex = JSON.parse('{\ +"ironcalc":["",[["export",[],["_rels.rs","doc_props.rs","escape.rs","mod.rs","shared_strings.rs","styles.rs","workbook.rs","workbook_xml_rels.rs","worksheets.rs","xml_constants.rs"]],["import",[],["colors.rs","metadata.rs","mod.rs","shared_strings.rs","styles.rs","tables.rs","util.rs","workbook.rs","worksheets.rs"]]],["compare.rs","error.rs","lib.rs"]],\ +"ironcalc_base":["",[["expressions",[["lexer",[],["mod.rs","ranges.rs","structured_references.rs","util.rs"]],["parser",[],["mod.rs","move_formula.rs","stringify.rs","walk.rs"]],["utils",[],["mod.rs"]]],["mod.rs","token.rs","types.rs"]],["formatter",[],["dates.rs","format.rs","lexer.rs","mod.rs","parser.rs"]],["functions",[["engineering",[["transcendental",[],["bessel_i.rs","bessel_j0_y0.rs","bessel_j1_y1.rs","bessel_jn_yn.rs","bessel_k.rs","bessel_util.rs","erf.rs","mod.rs"]]],["bessel.rs","bit_operations.rs","complex.rs","convert.rs","misc.rs","mod.rs","number_basis.rs"]]],["binary_search.rs","date_and_time.rs","financial.rs","financial_util.rs","information.rs","logical.rs","lookup_and_reference.rs","mathematical.rs","mod.rs","statistical.rs","subtotal.rs","text.rs","text_util.rs","util.rs","xlookup.rs"]],["language",[],["mod.rs"]],["locale",[],["mod.rs"]]],["actions.rs","calc_result.rs","cast.rs","cell.rs","constants.rs","diffs.rs","implicit_intersection.rs","lib.rs","model.rs","new_empty.rs","number_format.rs","styles.rs","types.rs","units.rs","utils.rs","workbook.rs","worksheet.rs"]],\ +"test":["",[],["test.rs"]]\ +}'); +createSrcSidebar(); diff --git a/src/ironcalc/compare.rs.html b/src/ironcalc/compare.rs.html new file mode 100644 index 0000000..7f4c135 --- /dev/null +++ b/src/ironcalc/compare.rs.html @@ -0,0 +1,413 @@ +compare.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+
use std::path::Path;
+
+use ironcalc_base::cell::CellValue;
+use ironcalc_base::types::*;
+use ironcalc_base::{expressions::utils::number_to_column, model::Model};
+
+use crate::export::save_to_xlsx;
+use crate::import::load_model_from_xlsx;
+
+pub struct CompareError {
+    message: String,
+}
+
+type CompareResult<T> = std::result::Result<T, CompareError>;
+
+pub struct Diff {
+    pub sheet_name: String,
+    pub row: i32,
+    pub column: i32,
+    pub value1: Cell,
+    pub value2: Cell,
+    pub reason: String,
+}
+
+// TODO use f64::EPSILON
+const EPS: f64 = 5e-8;
+// const EPS: f64 = f64::EPSILON;
+
+fn numbers_are_close(x: f64, y: f64, eps: f64) -> bool {
+    let norm = (x * x + y * y).sqrt();
+    if norm == 0.0 {
+        return true;
+    }
+    let d = f64::abs(x - y);
+    if d < eps {
+        return true;
+    }
+    d / norm < eps
+}
+/// Compares two Models in the internal representation and returns a list of differences
+pub fn compare(model1: &Model, model2: &Model) -> CompareResult<Vec<Diff>> {
+    let ws1 = model1.workbook.get_worksheet_names();
+    let ws2 = model2.workbook.get_worksheet_names();
+    if ws1.len() != ws2.len() {
+        return Err(CompareError {
+            message: "Different number of sheets".to_string(),
+        });
+    }
+    let eps = if let Ok(CellValue::Number(v)) = model1.get_cell_value_by_ref("METADATA!A1") {
+        v
+    } else {
+        EPS
+    };
+    let mut diffs = Vec::new();
+    let cells = model1.get_all_cells();
+    for cell in cells {
+        let sheet = cell.index;
+        let row = cell.row;
+        let column = cell.column;
+        let cell1 = &model1
+            .workbook
+            .worksheet(sheet)
+            .unwrap()
+            .cell(row, column)
+            .cloned()
+            .unwrap_or_default();
+        let cell2 = &model2
+            .workbook
+            .worksheet(sheet)
+            .unwrap()
+            .cell(row, column)
+            .cloned()
+            .unwrap_or_default();
+        match (cell1, cell2) {
+            (Cell::EmptyCell { .. }, Cell::EmptyCell { .. }) => {}
+            (Cell::NumberCell { .. }, Cell::NumberCell { .. }) => {}
+            (Cell::BooleanCell { .. }, Cell::BooleanCell { .. }) => {}
+            (Cell::ErrorCell { .. }, Cell::ErrorCell { .. }) => {}
+            (Cell::SharedString { .. }, Cell::SharedString { .. }) => {}
+            (
+                Cell::CellFormulaNumber { v: value1, .. },
+                Cell::CellFormulaNumber { v: value2, .. },
+            ) => {
+                if !numbers_are_close(*value1, *value2, eps) {
+                    diffs.push(Diff {
+                        sheet_name: ws1[cell.index as usize].clone(),
+                        row,
+                        column,
+                        value1: cell1.clone(),
+                        value2: cell2.clone(),
+                        reason: "Numbers are different".to_string(),
+                    });
+                }
+            }
+            (
+                Cell::CellFormulaString { v: value1, .. },
+                Cell::CellFormulaString { v: value2, .. },
+            ) => {
+                // FIXME: We should compare the actual value, not just the index
+                if value1 != value2 {
+                    diffs.push(Diff {
+                        sheet_name: ws1[cell.index as usize].clone(),
+                        row,
+                        column,
+                        value1: cell1.clone(),
+                        value2: cell2.clone(),
+                        reason: "Strings are different".to_string(),
+                    });
+                }
+            }
+            (
+                Cell::CellFormulaBoolean { v: value1, .. },
+                Cell::CellFormulaBoolean { v: value2, .. },
+            ) => {
+                // FIXME: We should compare the actual value, not just the index
+                if value1 != value2 {
+                    diffs.push(Diff {
+                        sheet_name: ws1[cell.index as usize].clone(),
+                        row,
+                        column,
+                        value1: cell1.clone(),
+                        value2: cell2.clone(),
+                        reason: "Booleans are different".to_string(),
+                    });
+                }
+            }
+            (
+                Cell::CellFormulaError { ei: index1, .. },
+                Cell::CellFormulaError { ei: index2, .. },
+            ) => {
+                // FIXME: We should compare the actual value, not just the index
+                if index1 != index2 {
+                    diffs.push(Diff {
+                        sheet_name: ws1[cell.index as usize].clone(),
+                        row,
+                        column,
+                        value1: cell1.clone(),
+                        value2: cell2.clone(),
+                        reason: "Errors are different".to_string(),
+                    });
+                }
+            }
+            (_, _) => {
+                diffs.push(Diff {
+                    sheet_name: ws1[cell.index as usize].clone(),
+                    row,
+                    column,
+                    value1: cell1.clone(),
+                    value2: cell2.clone(),
+                    reason: "Types are different".to_string(),
+                });
+            }
+        }
+    }
+    Ok(diffs)
+}
+
+pub(crate) fn compare_models(m1: &Model, m2: &Model) -> Result<(), String> {
+    match compare(m1, m2) {
+        Ok(diffs) => {
+            if diffs.is_empty() {
+                Ok(())
+            } else {
+                let mut message = "".to_string();
+                for diff in diffs {
+                    message = format!(
+                        "{}\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.reason
+                    );
+                }
+                Err(format!("Models are different: {}", message))
+            }
+        }
+        Err(r) => Err(format!("Models are different: {}", r.message)),
+    }
+}
+
+/// 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();
+    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 base_name = Path::new(file_path).file_name().unwrap().to_str().unwrap();
+
+    let temp_path_buff = temp_dir_name.join(base_name);
+    let temp_file_path = &format!("{}.xlsx", temp_path_buff.to_str().unwrap());
+    // 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();
+    model2.evaluate();
+    compare_models(&model1, &model2)
+}
+
\ No newline at end of file diff --git a/src/ironcalc/error.rs.html b/src/ironcalc/error.rs.html new file mode 100644 index 0000000..a163d6b --- /dev/null +++ b/src/ironcalc/error.rs.html @@ -0,0 +1,179 @@ +error.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+
use std::io;
+use std::num::{ParseFloatError, ParseIntError};
+use thiserror::Error;
+use zip::result::ZipError;
+
+#[derive(Error, Debug, PartialEq, Eq)]
+pub enum XlsxError {
+    #[error("I/O Error: {0}")]
+    IO(String),
+    #[error("Zip Error: {0}")]
+    Zip(String),
+    #[error("XML Error: {0}")]
+    Xml(String),
+    #[error("{0}")]
+    Workbook(String),
+    #[error("Evaluation Error: {}", .0.join("; "))]
+    Evaluation(Vec<String>),
+    #[error("Comparison Error: {0}")]
+    Comparison(String),
+    #[error("Not Implemented Error: {0}")]
+    NotImplemented(String),
+}
+
+impl From<io::Error> for XlsxError {
+    fn from(error: io::Error) -> Self {
+        XlsxError::IO(error.to_string())
+    }
+}
+
+impl From<ZipError> for XlsxError {
+    fn from(error: ZipError) -> Self {
+        XlsxError::Zip(error.to_string())
+    }
+}
+
+impl From<ParseIntError> for XlsxError {
+    fn from(error: ParseIntError) -> Self {
+        XlsxError::Xml(error.to_string())
+    }
+}
+
+impl From<ParseFloatError> for XlsxError {
+    fn from(error: ParseFloatError) -> Self {
+        XlsxError::Xml(error.to_string())
+    }
+}
+
+impl From<roxmltree::Error> for XlsxError {
+    fn from(error: roxmltree::Error) -> Self {
+        XlsxError::Xml(error.to_string())
+    }
+}
+
+impl XlsxError {
+    pub fn user_message(&self) -> String {
+        match &self {
+            XlsxError::IO(_) | XlsxError::Workbook(_) => self.to_string(),
+            XlsxError::Zip(_) | XlsxError::Xml(_) => {
+                "IronCalc can only open workbooks created by Microsoft Excel. \
+                Can you open this file with Excel, save it to a new file, \
+                and then open that new file with IronCalc? If you've already tried this, \
+                then send this workbook to support@ironcalc.com and our engineering team \
+                will work with you to fix the issue."
+                    .to_string()
+            }
+            XlsxError::NotImplemented(error) => format!(
+                "IronCalc cannot open this workbook due to the following unsupported features: \
+                {error}. You can either re-implement these parts of your workbook using features \
+                supported by IronCalc, or you can send this workbook to support@ironcalc.com \
+                and our engineering team will work with you to fix the issue.",
+            ),
+            XlsxError::Evaluation(errors) => format!(
+                "IronCalc could not evaluate this workbook without errors. This may indicate a bug or missing feature \
+                in the IronCalc spreadsheet calculation engine. Please contact support@ironcalc.com, share the entirety \
+                of this error message and the relevant workbook, and we will work with you to resolve the issue. \
+                Detailed error message:\n{}",
+                errors.join("\n")
+            ),
+            XlsxError::Comparison(error) => format!(
+                "IronCalc produces different results when evaluating the workbook \
+                than those already present in the workbook. This may indicate a bug or missing \
+                feature in the IronCalc spreadsheet calculation engine. Please contact \
+                support@ironcalc.com, share the entirety of this error message and the relevant \
+                workbook, and we will work with you to resolve the issue. \
+                Detailed error message:\n{error}"
+            ),
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc/export/_rels.rs.html b/src/ironcalc/export/_rels.rs.html new file mode 100644 index 0000000..6d46468 --- /dev/null +++ b/src/ironcalc/export/_rels.rs.html @@ -0,0 +1,13 @@ +_rels.rs - source
1
+2
+3
+4
+5
+6
+
use ironcalc_base::types::Workbook;
+
+pub(crate) fn get_dot_rels(_: &Workbook) -> String {
+    r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>"#.to_owned()
+}
+
\ No newline at end of file diff --git a/src/ironcalc/export/doc_props.rs.html b/src/ironcalc/export/doc_props.rs.html new file mode 100644 index 0000000..5578963 --- /dev/null +++ b/src/ironcalc/export/doc_props.rs.html @@ -0,0 +1,139 @@ +doc_props.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+
use chrono::NaiveDateTime;
+use ironcalc_base::{
+    new_empty::{APPLICATION, APP_VERSION, IRONCALC_USER},
+    types::Workbook,
+};
+
+use crate::error::XlsxError;
+
+// Application-Defined File Properties part
+pub(crate) fn get_app_xml(_: &Workbook) -> String {
+    // contains application name and version
+
+    // The next few are not needed:
+    // security. It is password protected (not implemented)
+    // Scale
+    // Titles of parts
+
+    format!(
+        "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>
+<Properties xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties\" \
+            xmlns:vt=\"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes\">\
+  <Application>{}</Application>\
+  <AppVersion>{}</AppVersion>\
+</Properties>",
+        APPLICATION, APP_VERSION
+    )
+}
+
+// Core File Properties part
+pub(crate) fn get_core_xml(workbook: &Workbook, milliseconds: i64) -> Result<String, XlsxError> {
+    // contains the name of the creator, last modified and date
+    let metadata = &workbook.metadata;
+    let creator = metadata.creator.to_string();
+    let last_modified_by = IRONCALC_USER.to_string();
+    let created = metadata.created.to_string();
+    // FIXME add now
+
+    let seconds = milliseconds / 1000;
+    let dt = match NaiveDateTime::from_timestamp_opt(seconds, 0) {
+        Some(s) => s,
+        None => {
+            return Err(XlsxError::Xml(format!(
+                "Invalid timestamp: {}",
+                milliseconds
+            )))
+        }
+    };
+    let last_modified = dt.format("%Y-%m-%dT%H:%M:%SZ").to_string();
+    Ok(format!(
+        "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>
+<cp:coreProperties \
+ xmlns:cp=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\" \
+ xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:dcterms=\"http://purl.org/dc/terms/\" \
+ xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" \
+ xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"> \
+<dc:title></dc:title><dc:subject></dc:subject>\
+<dc:creator>{}</dc:creator>\
+<cp:keywords></cp:keywords>\
+<dc:description></dc:description>\
+<cp:lastModifiedBy>{}</cp:lastModifiedBy>\
+<cp:revision></cp:revision>\
+<dcterms:created xsi:type=\"dcterms:W3CDTF\">{}</dcterms:created>\
+<dcterms:modified xsi:type=\"dcterms:W3CDTF\">{}</dcterms:modified>\
+<cp:category></cp:category>\
+<cp:contentStatus></cp:contentStatus>\
+</cp:coreProperties>",
+        creator, last_modified_by, created, last_modified
+    ))
+}
+
\ No newline at end of file diff --git a/src/ironcalc/export/escape.rs.html b/src/ironcalc/export/escape.rs.html new file mode 100644 index 0000000..1d6f71f --- /dev/null +++ b/src/ironcalc/export/escape.rs.html @@ -0,0 +1,199 @@ +escape.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+
// Taken from :
+
+// https://docs.rs/xml-rs/latest/src/xml/escape.rs.html#1-125
+
+//! Contains functions for performing XML special characters escaping.
+
+use std::borrow::Cow;
+
+enum Value {
+    Char(char),
+    Str(&'static str),
+}
+
+fn escape_char(c: char) -> Value {
+    match c {
+        '<' => Value::Str("&lt;"),
+        '>' => Value::Str("&gt;"),
+        '"' => Value::Str("&quot;"),
+        '\'' => Value::Str("&apos;"),
+        '&' => Value::Str("&amp;"),
+        '\n' => Value::Str("&#xA;"),
+        '\r' => Value::Str("&#xD;"),
+        _ => Value::Char(c),
+    }
+}
+
+enum Process<'a> {
+    Borrowed(&'a str),
+    Owned(String),
+}
+
+impl<'a> Process<'a> {
+    fn process(&mut self, (i, next): (usize, Value)) {
+        match next {
+            Value::Str(s) => match *self {
+                Process::Owned(ref mut o) => o.push_str(s),
+                Process::Borrowed(b) => {
+                    let mut r = String::with_capacity(b.len() + s.len());
+                    r.push_str(&b[..i]);
+                    r.push_str(s);
+                    *self = Process::Owned(r);
+                }
+            },
+            Value::Char(c) => match *self {
+                Process::Borrowed(_) => {}
+                Process::Owned(ref mut o) => o.push(c),
+            },
+        }
+    }
+
+    fn into_result(self) -> Cow<'a, str> {
+        match self {
+            Process::Borrowed(b) => Cow::Borrowed(b),
+            Process::Owned(o) => Cow::Owned(o),
+        }
+    }
+}
+
+impl<'a> Extend<(usize, Value)> for Process<'a> {
+    fn extend<I: IntoIterator<Item = (usize, Value)>>(&mut self, it: I) {
+        for v in it.into_iter() {
+            self.process(v);
+        }
+    }
+}
+
+/// Performs escaping of common XML characters inside an attribute value.
+///
+/// This function replaces several important markup characters with their
+/// entity equivalents:
+///
+/// * `<` → `&lt;`
+/// * `>` → `&gt;`
+/// * `"` → `&quot;`
+/// * `'` → `&apos;`
+/// * `&` → `&amp;`
+///
+/// The resulting string is safe to use inside XML attribute values.
+///
+/// Does not perform allocations if the given string does not contain escapable characters.
+pub fn escape_xml(s: &str) -> Cow<str> {
+    let mut p = Process::Borrowed(s);
+    p.extend(s.char_indices().map(|(ind, c)| (ind, escape_char(c))));
+    p.into_result()
+}
+
+// A simpler function that allocates memory for each replacement
+// fn escape_xml(value: &str) -> String {
+//     value
+//         .replace('&', "&amp")
+//         .replace('<', "&lt;")
+//         .replace('>', "&gt;")
+//         .replace('"', "&quot;")
+//         .replace('\'', "&apos;")
+// }
+
+// See also:
+// https://docs.rs/shell-escape/0.1.5/src/shell_escape/lib.rs.html#17-23
+// https://aaronerhardt.github.io/docs/relm4/src/quick_xml/escapei.rs.html#69-106
+
\ No newline at end of file diff --git a/src/ironcalc/export/mod.rs.html b/src/ironcalc/export/mod.rs.html new file mode 100644 index 0000000..99d8a9a --- /dev/null +++ b/src/ironcalc/export/mod.rs.html @@ -0,0 +1,275 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+
mod _rels;
+mod doc_props;
+mod escape;
+mod shared_strings;
+mod styles;
+mod workbook;
+mod workbook_xml_rels;
+mod worksheets;
+mod xml_constants;
+
+use std::io::BufWriter;
+use std::{
+    fs,
+    io::{Seek, Write},
+};
+
+use ironcalc_base::expressions::utils::number_to_column;
+use ironcalc_base::model::{get_milliseconds_since_epoch, Model};
+use ironcalc_base::types::Workbook;
+
+use self::xml_constants::XML_DECLARATION;
+
+use crate::error::XlsxError;
+
+#[cfg(test)]
+mod test;
+
+fn get_content_types_xml(workbook: &Workbook) -> String {
+    // A list of all files in the zip
+    let mut content = vec![
+        r#"<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">"#.to_string(),
+        r#"<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>"#.to_string(),
+        r#"<Default Extension="xml" ContentType="application/xml"/>"#.to_string(),
+        r#"<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>"#.to_string(),
+    ];
+    for worksheet in 0..workbook.worksheets.len() {
+        let sheet = format!(
+            r#"<Override PartName="/xl/worksheets/sheet{}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>"#,
+            worksheet + 1
+        );
+        content.push(sheet);
+    }
+    // we skip the theme and calcChain
+    // r#"<Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>"#,
+    // r#"<Override PartName="/xl/calcChain.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml"/>"#,
+    content.extend([
+        r#"<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>"#.to_string(),
+        r#"<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>"#.to_string(),
+        r#"<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>"#.to_string(),
+        r#"<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>"#.to_string(),
+        r#"</Types>"#.to_string(),
+    ]);
+    format!("{XML_DECLARATION}\n{}", content.join(""))
+}
+
+/// Exports a model to an xlsx file
+pub fn save_to_xlsx(model: &Model, file_name: &str) -> Result<(), XlsxError> {
+    let file_path = std::path::Path::new(&file_name);
+    if file_path.exists() {
+        return Err(XlsxError::IO(format!("file {} already exists", file_name)));
+    }
+    let file = fs::File::create(file_path).unwrap();
+    let writer = BufWriter::new(file);
+    save_xlsx_to_writer(model, writer)?;
+
+    Ok(())
+}
+
+pub fn save_xlsx_to_writer<W: Write + Seek>(model: &Model, writer: W) -> Result<W, XlsxError> {
+    let workbook = &model.workbook;
+    let mut zip = zip::ZipWriter::new(writer);
+
+    let options = zip::write::FileOptions::default();
+
+    // root folder
+    zip.start_file("[Content_Types].xml", options)?;
+    zip.write_all(get_content_types_xml(workbook).as_bytes())?;
+
+    zip.add_directory("docProps", options)?;
+    zip.start_file("docProps/app.xml", options)?;
+    zip.write_all(doc_props::get_app_xml(workbook).as_bytes())?;
+    zip.start_file("docProps/core.xml", options)?;
+    let milliseconds = get_milliseconds_since_epoch();
+    zip.write_all(doc_props::get_core_xml(workbook, milliseconds)?.as_bytes())?;
+
+    // Package-relationship item
+    zip.add_directory("_rels", options)?;
+    zip.start_file("_rels/.rels", options)?;
+    zip.write_all(_rels::get_dot_rels(workbook).as_bytes())?;
+
+    zip.add_directory("xl", options)?;
+    zip.start_file("xl/sharedStrings.xml", options)?;
+    zip.write_all(shared_strings::get_shared_strings_xml(workbook).as_bytes())?;
+    zip.start_file("xl/styles.xml", options)?;
+    zip.write_all(styles::get_styles_xml(workbook).as_bytes())?;
+    zip.start_file("xl/workbook.xml", options)?;
+    zip.write_all(workbook::get_workbook_xml(workbook).as_bytes())?;
+
+    zip.add_directory("xl/_rels", options)?;
+    zip.start_file("xl/_rels/workbook.xml.rels", options)?;
+    zip.write_all(workbook_xml_rels::get_workbook_xml_rels(workbook).as_bytes())?;
+
+    zip.add_directory("xl/worksheets", options)?;
+    for (sheet_index, worksheet) in workbook.worksheets.iter().enumerate() {
+        let id = sheet_index + 1;
+        zip.start_file(&format!("xl/worksheets/sheet{id}.xml"), options)?;
+        let dimension = model
+            .workbook
+            .worksheet(sheet_index as u32)
+            .unwrap()
+            .dimension();
+        let column_min_str = number_to_column(dimension.min_column).unwrap();
+        let column_max_str = number_to_column(dimension.max_column).unwrap();
+        let min_row = dimension.min_row;
+        let max_row = dimension.max_row;
+        let sheet_dimension_str = &format!("{column_min_str}{min_row}:{column_max_str}{max_row}");
+        zip.write_all(
+            worksheets::get_worksheet_xml(
+                worksheet,
+                &model.parsed_formulas[sheet_index],
+                sheet_dimension_str,
+            )
+            .as_bytes(),
+        )?;
+    }
+
+    let writer = zip.finish()?;
+    Ok(writer)
+}
+
+/// 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();
+    let file_path = std::path::Path::new(output);
+    let mut file = fs::File::create(file_path).unwrap();
+    file.write_all(s.as_bytes()).unwrap();
+}
+
\ No newline at end of file diff --git a/src/ironcalc/export/shared_strings.rs.html b/src/ironcalc/export/shared_strings.rs.html new file mode 100644 index 0000000..1105335 --- /dev/null +++ b/src/ironcalc/export/shared_strings.rs.html @@ -0,0 +1,33 @@ +shared_strings.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
use ironcalc_base::types::Workbook;
+
+use super::{escape::escape_xml, xml_constants::XML_DECLARATION};
+
+pub(crate) fn get_shared_strings_xml(model: &Workbook) -> String {
+    let mut shared_strings: Vec<String> = vec![];
+    let count = &model.shared_strings.len();
+    let unique_count = &model.shared_strings.len();
+    for shared_string in &model.shared_strings {
+        shared_strings.push(format!("<si><t>{}</t></si>", escape_xml(shared_string)));
+    }
+    format!("{}\n\
+      <sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" count=\"{count}\" uniqueCount=\"{unique_count}\">\
+        {}\
+      </sst>", XML_DECLARATION, shared_strings.join(""))
+}
+
\ No newline at end of file diff --git a/src/ironcalc/export/styles.rs.html b/src/ironcalc/export/styles.rs.html new file mode 100644 index 0000000..313a5c3 --- /dev/null +++ b/src/ironcalc/export/styles.rs.html @@ -0,0 +1,565 @@ +styles.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+
use ironcalc_base::types::{
+    Alignment, BorderItem, HorizontalAlignment, Styles, VerticalAlignment, Workbook,
+};
+
+use super::{escape::escape_xml, xml_constants::XML_DECLARATION};
+
+fn get_fonts_xml(styles: &Styles) -> String {
+    let fonts = &styles.fonts;
+    let mut fonts_str: Vec<String> = vec![];
+    for font in fonts {
+        let size = format!("<sz val=\"{}\"/>", font.sz);
+        let color = if let Some(some_color) = &font.color {
+            format!("<color rgb=\"FF{}\"/>", some_color.trim_start_matches('#'))
+        } else {
+            "".to_string()
+        };
+        let name = format!("<name val=\"{}\"/>", escape_xml(&font.name));
+        let bold = if font.b { "<b/>" } else { "" };
+        let italic = if font.i { "<i/>" } else { "" };
+        let underline = if font.u { "<u/>" } else { "" };
+        let strike = if font.strike { "<strike/>" } else { "" };
+        let family = format!("<family val=\"{}\"/>", font.family);
+        let scheme = format!("<scheme val=\"{}\"/>", font.scheme);
+        fonts_str.push(format!(
+            "<font>\
+                {size}\
+                {color}\
+                {name}\
+                {bold}\
+                {italic}\
+                {underline}\
+                {strike}\
+                {family}\
+                {scheme}\
+             </font>"
+        ));
+    }
+    let font_count = fonts.len();
+    format!(
+        "<fonts count=\"{font_count}\">{}</fonts>",
+        fonts_str.join("")
+    )
+}
+
+fn get_color_xml(color: &Option<String>, name: &str) -> String {
+    // We blindly append FF at the beginning of these RGB color to make it ARGB
+    if let Some(some_color) = color {
+        format!("<{name} rgb=\"FF{}\"/>", some_color.trim_start_matches('#'))
+    } else {
+        "".to_string()
+    }
+}
+
+fn get_fills_xml(styles: &Styles) -> String {
+    let fills = &styles.fills;
+    let mut fills_str: Vec<String> = vec![];
+    for fill in fills {
+        let pattern_type = &fill.pattern_type;
+        let fg_color = get_color_xml(&fill.fg_color, "fgColor");
+        let bg_color = get_color_xml(&fill.bg_color, "bgColor");
+        fills_str.push(format!(
+            "<fill><patternFill patternType=\"{pattern_type}\">{fg_color}{bg_color}</patternFill></fill>"
+        ));
+    }
+    let fill_count = fills.len();
+    format!(
+        "<fills count=\"{fill_count}\">{}</fills>",
+        fills_str.join("")
+    )
+}
+
+fn get_border_xml(border: &Option<BorderItem>, name: &str) -> String {
+    if let Some(border_item) = border {
+        let color = get_color_xml(&border_item.color, "color");
+        return format!("<{name} style=\"{}\">{color}</{name}>", border_item.style);
+    }
+    format!("<{name}/>")
+}
+
+fn get_borders_xml(styles: &Styles) -> String {
+    let borders = &styles.borders;
+    let mut borders_str: Vec<String> = vec![];
+    let border_count = borders.len();
+    for border in borders {
+        // TODO: diagonal_up/diagonal_down?
+        let border_left = get_border_xml(&border.left, "left");
+        let border_right = get_border_xml(&border.right, "right");
+        let border_top = get_border_xml(&border.top, "top");
+        let border_bottom = get_border_xml(&border.bottom, "bottom");
+        let border_diagonal = get_border_xml(&border.diagonal, "diagonal");
+        borders_str.push(format!(
+            "<border>{border_left}{border_right}{border_top}{border_bottom}{border_diagonal}</border>"
+        ));
+    }
+    format!(
+        "<borders count=\"{border_count}\">{}</borders>",
+        borders_str.join("")
+    )
+}
+
+// <numFmts count="1">
+//   <numFmt numFmtId="164" formatCode="##,#00;[Blue]\-\-#,##0"/>
+// </numFmts>
+fn get_cell_number_formats_xml(styles: &Styles) -> String {
+    let num_fmts = &styles.num_fmts;
+    let mut num_fmts_str: Vec<String> = vec![];
+    let num_fmt_count = num_fmts.len();
+    for num_fmt in num_fmts {
+        let num_fmt_id = num_fmt.num_fmt_id;
+        let format_code = &num_fmt.format_code;
+        let format_code = escape_xml(format_code);
+        num_fmts_str.push(format!(
+            "<numFmt numFmtId=\"{num_fmt_id}\" formatCode=\"{format_code}\"/>"
+        ));
+    }
+    if num_fmt_count == 0 {
+        return "".to_string();
+    }
+    format!(
+        "<numFmts count=\"{num_fmt_count}\">{}</numFmts>",
+        num_fmts_str.join("")
+    )
+}
+
+fn get_alignment(alignment: &Alignment) -> String {
+    let wrap_text = if alignment.wrap_text {
+        " wrapText=\"1\""
+    } else {
+        ""
+    };
+    let horizontal = if alignment.horizontal != HorizontalAlignment::default() {
+        format!(" horizontal=\"{}\"", alignment.horizontal)
+    } else {
+        "".to_string()
+    };
+    let vertical = if alignment.vertical != VerticalAlignment::default() {
+        format!(" vertical=\"{}\"", alignment.vertical)
+    } else {
+        "".to_string()
+    };
+    format!("<alignment{wrap_text}{horizontal}{vertical}/>")
+}
+
+fn get_cell_style_xfs_xml(styles: &Styles) -> String {
+    let cell_style_xfs = &styles.cell_style_xfs;
+    let mut cell_style_str: Vec<String> = vec![];
+    for cell_style_xf in cell_style_xfs {
+        let border_id = cell_style_xf.border_id;
+        let fill_id = cell_style_xf.fill_id;
+        let font_id = cell_style_xf.font_id;
+        let num_fmt_id = cell_style_xf.num_fmt_id;
+        let apply_alignment_str = if cell_style_xf.apply_alignment {
+            r#" applyAlignment="1""#
+        } else {
+            ""
+        };
+        let apply_font_str = if cell_style_xf.apply_font {
+            r#" applyFont="1""#
+        } else {
+            ""
+        };
+        let apply_fill_str = if cell_style_xf.apply_fill {
+            r#" applyFill="1""#
+        } else {
+            ""
+        };
+        cell_style_str.push(format!(
+            "<xf \
+              borderId=\"{border_id}\" \
+              fillId=\"{fill_id}\" \
+              fontId=\"{font_id}\" \
+              numFmtId=\"{num_fmt_id}\"\
+              {apply_alignment_str}\
+              {apply_font_str}\
+              {apply_fill_str}/>"
+        ));
+    }
+    let style_count = cell_style_xfs.len();
+    format!(
+        "<cellStyleXfs count=\"{style_count}\">{}</cellStyleXfs>",
+        cell_style_str.join("")
+    )
+}
+
+fn get_cell_xfs_xml(styles: &Styles) -> String {
+    let cell_xfs = &styles.cell_xfs;
+    let mut cell_xfs_str: Vec<String> = vec![];
+    for cell_xf in cell_xfs {
+        let xf_id = cell_xf.xf_id;
+        let border_id = cell_xf.border_id;
+        let fill_id = cell_xf.fill_id;
+        let font_id = cell_xf.font_id;
+        let num_fmt_id = cell_xf.num_fmt_id;
+        let quote_prefix_str = if cell_xf.quote_prefix {
+            r#" quotePrefix="1""#
+        } else {
+            ""
+        };
+        let apply_alignment_str = if cell_xf.apply_alignment {
+            r#" applyAlignment="1""#
+        } else {
+            ""
+        };
+        let apply_font_str = if cell_xf.apply_font {
+            r#" applyFont="1""#
+        } else {
+            ""
+        };
+        let apply_fill_str = if cell_xf.apply_fill {
+            r#" applyFill="1""#
+        } else {
+            ""
+        };
+        let properties = format!(
+            "xfId=\"{xf_id}\" \
+                borderId=\"{border_id}\" \
+                fillId=\"{fill_id}\" \
+                fontId=\"{font_id}\" \
+                numFmtId=\"{num_fmt_id}\"\
+                {quote_prefix_str}\
+                {apply_alignment_str}\
+                {apply_font_str}\
+                {apply_fill_str}"
+        );
+        if let Some(alignment) = &cell_xf.alignment {
+            let alignment = get_alignment(alignment);
+            cell_xfs_str.push(format!("<xf {properties}>{alignment}</xf>"));
+        } else {
+            cell_xfs_str.push(format!("<xf {properties}/>"));
+        }
+    }
+    let style_count = cell_xfs.len();
+    format!(
+        "<cellXfs count=\"{style_count}\">{}</cellXfs>",
+        cell_xfs_str.join("")
+    )
+}
+
+// <cellStyle xfId="0" name="Normal" builtinId="0"/>
+fn get_cell_styles_xml(styles: &Styles) -> String {
+    let cell_styles = &styles.cell_styles;
+    let mut cell_styles_str: Vec<String> = vec![];
+    for cell_style in cell_styles {
+        let xf_id = cell_style.xf_id;
+        let name = &cell_style.name;
+        let name = escape_xml(name);
+        let builtin_id = cell_style.builtin_id;
+        cell_styles_str.push(format!(
+            "<cellStyle xfId=\"{xf_id}\" name=\"{name}\" builtinId=\"{builtin_id}\"/>"
+        ));
+    }
+    let style_count = cell_styles.len();
+    format!(
+        "<cellStyles count=\"{style_count}\">{}</cellStyles>",
+        cell_styles_str.join("")
+    )
+}
+
+pub(crate) fn get_styles_xml(model: &Workbook) -> String {
+    let styles = &model.styles;
+    let fonts = get_fonts_xml(styles);
+    let fills = get_fills_xml(styles);
+    let borders = get_borders_xml(styles);
+    let number_formats = get_cell_number_formats_xml(styles);
+    let cell_style_xfs = get_cell_style_xfs_xml(styles);
+    let cell_xfs = get_cell_xfs_xml(styles);
+    let cell_styles = get_cell_styles_xml(styles);
+
+    format!(
+        "{XML_DECLARATION}
+<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">\
+{number_formats}\
+{fonts}\
+{fills}\
+{borders}\
+{cell_style_xfs}\
+{cell_xfs}\
+{cell_styles}\
+<dxfs count=\"0\"/>\
+</styleSheet>"
+    )
+}
+
\ No newline at end of file diff --git a/src/ironcalc/export/workbook.rs.html b/src/ironcalc/export/workbook.rs.html new file mode 100644 index 0000000..b689e0f --- /dev/null +++ b/src/ironcalc/export/workbook.rs.html @@ -0,0 +1,183 @@ +workbook.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+
//! <sheet name="Sheet1" sheetId="1" r:id="rId1"/>
+
+//! A workbook is composed of workbook-level properties and a collection of 1 or more sheets.
+//! The workbook part and corresponding properties comprise data
+//! used to set application and workbook-level operational state. The workbook also serves to bind all the sheets
+//! and child elements into an organized single file. The workbook XML attributes and elements include information
+//! about what application last saved the file, where and how the windows of the workbook were positioned, and
+//! an enumeration of the worksheets in the workbook.
+//! This is the XML for the smallest possible (blank) workbook:
+//!
+//! <workbook>
+//!   <sheets>
+//!     <sheet name="Sheet1" sheetId="1" r:id="rId1"/>
+//!   </sheets>
+//! </workbook>
+//!
+//! Note that this workbook has a single sheet, named Sheet1. An Id for the sheet is required, and a relationship Id
+//! pointing to the location of the sheet definition is also required.
+//!
+//!
+//!
+//! The most important objet of this part is a collection of all the sheets and all the defined names
+//! of the workbook.
+//!
+//! It also may hold state properties like the selected tab
+
+//! # bookViews
+//!
+
+use std::collections::HashMap;
+
+use ironcalc_base::types::{SheetState, Workbook};
+
+use super::escape::escape_xml;
+use super::xml_constants::XML_DECLARATION;
+
+pub(crate) fn get_workbook_xml(workbook: &Workbook) -> String {
+    // sheets
+    // <sheet name="Sheet1" sheetId="1" r:id="rId1"/>
+    let mut sheets_str: Vec<String> = vec![];
+    let mut sheet_id_to_sheet_index: HashMap<u32, u32> = HashMap::new();
+    for (sheet_index, worksheet) in workbook.worksheets.iter().enumerate() {
+        let name = &worksheet.name;
+        let name = escape_xml(name);
+        let sheet_id = worksheet.sheet_id;
+        let state_str = match &worksheet.state {
+            SheetState::Visible => "",
+            SheetState::Hidden => " state=\"hidden\"",
+            SheetState::VeryHidden => " state=\"veryHidden\"",
+        };
+
+        sheets_str.push(format!(
+            "<sheet name=\"{name}\" sheetId=\"{sheet_id}\" r:id=\"rId{}\"{state_str}/>",
+            sheet_index + 1
+        ));
+        sheet_id_to_sheet_index.insert(sheet_id, sheet_index as u32);
+    }
+
+    // defined names
+    // <definedName localSheetId="4" name="answer">shared!$G$5</definedName>
+    // <definedName name="numbers">Sheet1!$A$16:$A$18</definedName>
+    let mut defined_names_str: Vec<String> = vec![];
+    for defined_name in &workbook.defined_names {
+        let name = &defined_name.name;
+        let name = escape_xml(name);
+        let local_sheet_id = if let Some(sheet_id) = defined_name.sheet_id {
+            // In Excel the localSheetId is actually the index of the sheet.
+            let excel_local_sheet_id = sheet_id_to_sheet_index.get(&sheet_id).unwrap();
+            format!(" localSheetId=\"{excel_local_sheet_id}\"")
+        } else {
+            "".to_string()
+        };
+        let formula = escape_xml(&defined_name.formula);
+        defined_names_str.push(format!(
+            "<definedName name=\"{name}\"{local_sheet_id}>{formula}</definedName>"
+        ))
+    }
+
+    let sheets = sheets_str.join("");
+    let defined_names = defined_names_str.join("");
+    format!("{XML_DECLARATION}\n\
+    <workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">\
+      <sheets>\
+        {sheets}\
+      </sheets>\
+      <definedNames>\
+        {defined_names}\
+      </definedNames>\
+      <calcPr/>\
+    </workbook>")
+}
+
\ No newline at end of file diff --git a/src/ironcalc/export/workbook_xml_rels.rs.html b/src/ironcalc/export/workbook_xml_rels.rs.html new file mode 100644 index 0000000..c25ea76 --- /dev/null +++ b/src/ironcalc/export/workbook_xml_rels.rs.html @@ -0,0 +1,51 @@ +workbook_xml_rels.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+
use ironcalc_base::types::Workbook;
+
+use super::xml_constants::{XML_DECLARATION, XML_WORKSHEET};
+
+pub(crate) fn get_workbook_xml_rels(workbook: &Workbook) -> String {
+    let mut relationships_str: Vec<String> = vec![];
+    let worksheet_count = workbook.worksheets.len() + 1;
+    for id in 1..worksheet_count {
+        relationships_str.push(format!(
+            "<Relationship Id=\"rId{id}\" Type=\"{XML_WORKSHEET}\" Target=\"worksheets/sheet{id}.xml\"/>"
+        ));
+    }
+    let mut id = worksheet_count;
+    relationships_str.push(
+        format!("<Relationship Id=\"rId{id}\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/>")
+    );
+    id += 1;
+    relationships_str.push(
+        format!("<Relationship Id=\"rId{id}\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings\" Target=\"sharedStrings.xml\"/>")
+    );
+    format!(
+        "{XML_DECLARATION}\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">{}</Relationships>",
+        relationships_str.join("")
+    )
+}
+
\ No newline at end of file diff --git a/src/ironcalc/export/worksheets.rs.html b/src/ironcalc/export/worksheets.rs.html new file mode 100644 index 0000000..9e8c28f --- /dev/null +++ b/src/ironcalc/export/worksheets.rs.html @@ -0,0 +1,535 @@ +worksheets.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+
//! # A note on shared formulas
+//! Although both Excel and IronCalc uses shared formulas they are used in a slightly different way that cannot be mapped 1-1
+//! In IronCalc _all_ formulas are shared and there is a list of shared formulas much like there is a list of shared strings.
+//! In Excel the situation in more nuanced. A shared formula is shared amongst a rage of cells.
+//! The top left cell would be the "mother" cell that would contain the shared formula:
+//! <c r="F4" t="str">
+//!    <f t="shared" ref="F4:F8" si="42">A4+C4</f>
+//!    <v>123</v>
+//! </c>
+//! Cells in the range F4:F8 will then link to that formula like so:
+//! <c r="F6">
+//!   <f t="shared" si="42"/>
+//!   <v>1</v>
+//! </c>
+//! Formula in F6 would then be 'A6+C6'
+use std::collections::HashMap;
+
+use itertools::Itertools;
+
+use ironcalc_base::{
+    expressions::{
+        parser::{stringify::to_excel_string, Node},
+        types::CellReferenceRC,
+        utils::number_to_column,
+    },
+    types::{Cell, Worksheet},
+};
+
+use super::{escape::escape_xml, xml_constants::XML_DECLARATION};
+
+fn get_cell_style_attribute(s: i32) -> String {
+    if s == 0 {
+        "".to_string()
+    } else {
+        format!(" s=\"{}\"", s)
+    }
+}
+
+fn get_formula_attribute(
+    sheet_name: String,
+    row: i32,
+    column: i32,
+    parsed_formula: &Node,
+) -> String {
+    let cell_ref = CellReferenceRC {
+        sheet: sheet_name,
+        row,
+        column,
+    };
+    let formula = &to_excel_string(parsed_formula, &cell_ref);
+    escape_xml(formula).to_string()
+}
+
+pub(crate) fn get_worksheet_xml(
+    worksheet: &Worksheet,
+    parsed_formulas: &[Node],
+    dimension: &str,
+) -> String {
+    let mut sheet_data_str: Vec<String> = vec![];
+    let mut cols_str: Vec<String> = vec![];
+
+    for col in &worksheet.cols {
+        // <col min="4" max="4" width="12" customWidth="1"/>
+        let min = col.min;
+        let max = col.max;
+        let width = col.width;
+        let custom_width = i32::from(col.custom_width);
+        let column_style = match col.style {
+            Some(s) => format!(" style=\"{s}\""),
+            None => "".to_string(),
+        };
+        cols_str.push(format!(
+            "<col min=\"{min}\" max=\"{max}\" width=\"{width}\" customWidth=\"{custom_width}\"{column_style}/>"
+        ));
+    }
+
+    // this is a bit of an overkill. A dictionary of the row styles by row_index
+    let mut row_style_dict = HashMap::new();
+    for row in &worksheet.rows {
+        // {
+        //     "height": 13,
+        //     "r": 7,
+        //     "custom_format": false,
+        //     "custom_height": true,
+        //     "s": 0
+        //     "hidden": false,
+        //   },
+        row_style_dict.insert(row.r, row.clone());
+    }
+
+    for (row_index, row_data) in worksheet.sheet_data.iter().sorted_by_key(|x| x.0) {
+        let mut row_data_str: Vec<String> = vec![];
+        for (column_index, cell) in row_data.iter().sorted_by_key(|x| x.0) {
+            let column_name = number_to_column(*column_index).unwrap();
+            let cell_name = format!("{column_name}{row_index}");
+            match cell {
+                Cell::EmptyCell { s } => {
+                    // they only hold the style
+                    let style = get_cell_style_attribute(*s);
+                    row_data_str.push(format!("<c r=\"{cell_name}\"{style}/>"));
+                }
+                Cell::BooleanCell { v, s } => {
+                    // <c r="A8" t="b" s="1">
+                    //     <v>1</v>
+                    // </c>
+                    let b = i32::from(*v);
+                    let style = get_cell_style_attribute(*s);
+                    row_data_str.push(format!(
+                        "<c r=\"{cell_name}\" t=\"b\"{style}><v>{b}</v></c>"
+                    ));
+                }
+                Cell::NumberCell { v, s } => {
+                    // Normally the type number is left out. Example:
+                    // <c r="C6" s="1">
+                    //     <v>3</v>
+                    // </c>
+                    let style = get_cell_style_attribute(*s);
+                    row_data_str.push(format!("<c r=\"{cell_name}\"{style}><v>{v}</v></c>"));
+                }
+                Cell::ErrorCell { ei, s } => {
+                    let style = get_cell_style_attribute(*s);
+                    row_data_str.push(format!(
+                        "<c r=\"{cell_name}\" t=\"e\"{style}><v>{ei}</v></c>"
+                    ));
+                }
+                Cell::SharedString { si, s } => {
+                    // Example:
+                    // <c r="A1" s="1" t="s">
+                    //    <v>5</v>
+                    // </c>
+                    // Cell on A1 contains a string (t="s") of style="1". The string is the 6th in the list of shared strings
+                    let style = get_cell_style_attribute(*s);
+                    row_data_str.push(format!(
+                        "<c r=\"{cell_name}\" t=\"s\"{style}><v>{si}</v></c>"
+                    ));
+                }
+                Cell::CellFormula { f: _, s: _ } => {
+                    panic!("Model needs to be evaluated before saving!");
+                }
+                Cell::CellFormulaBoolean { f, v, s } => {
+                    // <c r="A4" t="b" s="3">
+                    //   <f>ISTEXT(A5)</f>
+                    //   <v>1</v>
+                    // </c>
+                    let style = get_cell_style_attribute(*s);
+
+                    let formula = get_formula_attribute(
+                        worksheet.get_name(),
+                        *row_index,
+                        *column_index,
+                        &parsed_formulas[*f as usize],
+                    );
+
+                    let b = i32::from(*v);
+                    row_data_str.push(format!(
+                        "<c r=\"{cell_name}\" t=\"b\"{style}><f>{formula}</f><v>{b}</v></c>"
+                    ));
+                }
+                Cell::CellFormulaNumber { f, v, s } => {
+                    // Note again type is skipped
+                    // <c r="C4" s="3">
+                    //   <f>A5+C3</f>
+                    //   <v>123</v>
+                    // </c>
+
+                    let formula = get_formula_attribute(
+                        worksheet.get_name(),
+                        *row_index,
+                        *column_index,
+                        &parsed_formulas[*f as usize],
+                    );
+                    let style = get_cell_style_attribute(*s);
+
+                    row_data_str.push(format!(
+                        "<c r=\"{cell_name}\"{style}><f>{formula}</f><v>{v}</v></c>"
+                    ));
+                }
+                Cell::CellFormulaString { f, v, s } => {
+                    // <c r="C6" t="str" s="5">
+                    //   <f>CONCATENATE(A1, A2)</f>
+                    //   <v>Hello world!</v>
+                    // </c>
+                    let formula = get_formula_attribute(
+                        worksheet.get_name(),
+                        *row_index,
+                        *column_index,
+                        &parsed_formulas[*f as usize],
+                    );
+                    let style = get_cell_style_attribute(*s);
+
+                    row_data_str.push(format!(
+                        "<c r=\"{cell_name}\" t=\"str\"{style}><f>{formula}</f><v>{v}</v></c>"
+                    ));
+                }
+                Cell::CellFormulaError {
+                    f,
+                    ei,
+                    s,
+                    o: _,
+                    m: _,
+                } => {
+                    // <c r="C6" t="e" s="4">
+                    //   <f>A1/A3<f/>
+                    //   <v>#DIV/0!</v>
+                    // </c>
+                    let formula = get_formula_attribute(
+                        worksheet.get_name(),
+                        *row_index,
+                        *column_index,
+                        &parsed_formulas[*f as usize],
+                    );
+                    let style = get_cell_style_attribute(*s);
+                    row_data_str.push(format!(
+                        "<c r=\"{cell_name}\" t=\"e\"{style}><f>{formula}</f><v>{ei}</v></c>"
+                    ));
+                }
+            }
+        }
+        let row_style_str = match row_style_dict.get(row_index) {
+            Some(row_style) => {
+                let hidden_str = if row_style.hidden {
+                    r#" hidden="1""#
+                } else {
+                    ""
+                };
+                format!(
+                    r#" s="{}" ht="{}" customHeight="{}" customFormat="{}"{}"#,
+                    row_style.s,
+                    row_style.height,
+                    i32::from(row_style.custom_height),
+                    i32::from(row_style.custom_format),
+                    hidden_str,
+                )
+            }
+            None => "".to_string(),
+        };
+        sheet_data_str.push(format!(
+            "<row r=\"{row_index}\"{row_style_str}>{}</row>",
+            row_data_str.join("")
+        ))
+    }
+    let sheet_data = sheet_data_str.join("");
+    let cols = cols_str.join("");
+    let cols = if cols.is_empty() {
+        "".to_string()
+    } else {
+        format!("<cols>{cols}</cols>")
+    };
+
+    format!(
+        "{XML_DECLARATION}
+<worksheet \
+xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" \
+xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">\
+  <dimension ref=\"{dimension}\"/>\
+  <sheetViews>\
+    <sheetView workbookViewId=\"0\">\
+        <selection activeCell=\"A1\" sqref=\"A1\"/>\
+    </sheetView>\
+  </sheetViews>\
+  {cols}\
+  <sheetData>\
+  {sheet_data}\
+  </sheetData>\
+</worksheet>"
+    )
+}
+
\ No newline at end of file diff --git a/src/ironcalc/export/xml_constants.rs.html b/src/ironcalc/export/xml_constants.rs.html new file mode 100644 index 0000000..fb24964 --- /dev/null +++ b/src/ironcalc/export/xml_constants.rs.html @@ -0,0 +1,11 @@ +xml_constants.rs - source
1
+2
+3
+4
+5
+
pub(crate) const XML_DECLARATION: &str =
+    r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>"#;
+
+pub(crate) const XML_WORKSHEET: &str =
+    r#"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"#;
+
\ No newline at end of file diff --git a/src/ironcalc/import/colors.rs.html b/src/ironcalc/import/colors.rs.html new file mode 100644 index 0000000..19dd983 --- /dev/null +++ b/src/ironcalc/import/colors.rs.html @@ -0,0 +1,515 @@ +colors.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+
use core::cmp::max;
+use core::cmp::min;
+
+// https://gist.github.com/emanuel-sanabria-developer/5793377
+// https://github.com/ClosedXML/ClosedXML/wiki/Excel-Indexed-Colors
+
+// Warning: Excel uses a weird normalization for HSL colors (0, 255)
+// We use a more standard one but our HSL numbers will not coincide with Excel's
+
+pub(crate) fn hex_to_rgb(h: &str) -> [i32; 3] {
+    let r = i32::from_str_radix(&h[1..3], 16).unwrap();
+    let g = i32::from_str_radix(&h[3..5], 16).unwrap();
+    let b = i32::from_str_radix(&h[5..7], 16).unwrap();
+    [r, g, b]
+}
+
+pub(crate) fn rgb_to_hex(rgb: [i32; 3]) -> String {
+    format!("#{:02X}{:02X}{:02X}", rgb[0], rgb[1], rgb[2])
+}
+
+pub(crate) fn rgb_to_hsl(rgb: [i32; 3]) -> [i32; 3] {
+    let r = rgb[0];
+    let g = rgb[1];
+    let b = rgb[2];
+    let red = r as f64 / 255.0;
+    let green = g as f64 / 255.0;
+    let blue = b as f64 / 255.0;
+    let max_color = max(max(r, g), b);
+    let min_color = min(min(r, g), b);
+    let chroma = (max_color - min_color) as f64 / 255.0;
+    if chroma == 0.0 {
+        return [0, 0, (red * 100.0).round() as i32];
+    }
+
+    let hue;
+    let luminosity = (max_color + min_color) as f64 / (255.0 * 2.0);
+    let saturation = if luminosity > 0.5 {
+        0.5 * chroma / (1.0 - luminosity)
+    } else {
+        0.5 * chroma / luminosity
+    };
+    if max_color == r {
+        if green >= blue {
+            hue = 60.0 * (green - blue) / chroma;
+        } else {
+            hue = ((green - blue) / chroma + 6.0) * 60.0;
+        }
+    } else if max_color == g {
+        hue = ((blue - red) / chroma + 2.0) * 60.0;
+    } else {
+        hue = ((red - green) / chroma + 4.0) * 60.0;
+    }
+    let hue = hue.round() as i32;
+    let saturation = (saturation * 100.0).round() as i32;
+    let luminosity = (luminosity * 100.0).round() as i32;
+    [hue, saturation, luminosity]
+}
+
+fn hue_to_rgb(p: f64, q: f64, t: f64) -> f64 {
+    let mut c = t;
+    if c < 0.0 {
+        c += 1.0;
+    }
+    if c > 1.0 {
+        c -= 1.0;
+    }
+    if c < 1.0 / 6.0 {
+        return p + (q - p) * 6.0 * t;
+    };
+    if c < 0.5 {
+        return q;
+    };
+    if c < 2.0 / 3.0 {
+        return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
+    };
+    p
+}
+
+pub(crate) fn hsl_to_rgb(hsl: [i32; 3]) -> [i32; 3] {
+    let hue = (hsl[0] as f64) / 360.0;
+    let saturation = (hsl[1] as f64) / 100.0;
+    let luminosity = (hsl[2] as f64) / 100.0;
+    let red;
+    let green;
+    let blue;
+
+    if saturation == 0.0 {
+        // achromatic
+        red = luminosity * 255.0;
+        green = luminosity * 255.0;
+        blue = luminosity * 255.0;
+    } else {
+        let q = if luminosity < 0.5 {
+            luminosity * (1.0 + saturation)
+        } else {
+            luminosity + saturation - luminosity * saturation
+        };
+        let p = 2.0 * luminosity - q;
+        red = 255.0 * hue_to_rgb(p, q, hue + 1.0 / 3.0);
+        green = 255.0 * hue_to_rgb(p, q, hue);
+        blue = 255.0 * hue_to_rgb(p, q, hue - 1.0 / 3.0);
+    }
+    [
+        red.round() as i32,
+        green.round() as i32,
+        blue.round() as i32,
+    ]
+}
+
+/* 18.8.3 bgColor tint algorithm */
+fn hex_with_tint_to_rgb(hex: &str, tint: f64) -> String {
+    if tint == 0.0 {
+        return hex.to_string();
+    }
+    let mut hsl = rgb_to_hsl(hex_to_rgb(hex));
+    let l = hsl[2] as f64;
+    if tint < 0.0 {
+        // Lum’ = Lum * (1.0 + tint)
+        hsl[2] = (l * (1.0 + tint)).round() as i32;
+    } else {
+        // HLSMAX here would be 100, for Excel 255
+        // Lum‘ = Lum * (1.0-tint) + (HLSMAX – HLSMAX * (1.0-tint))
+        hsl[2] = (l + (100.0 - l) * tint).round() as i32;
+    };
+    rgb_to_hex(hsl_to_rgb(hsl))
+}
+
+pub fn get_themed_color(theme: i32, tint: f64) -> String {
+    let color_theme = [
+        "#FFFFFF", "#000000", // "window",
+        "#E7E6E6", "#44546A", "#4472C4", "#ED7D31", "#A5A5A5", "#FFC000", "#5B9BD5", "#70AD47",
+        "#0563C1", "#954F72",
+    ];
+    hex_with_tint_to_rgb(color_theme[theme as usize], tint)
+}
+
+pub fn get_indexed_color(index: i32) -> String {
+    let color_list = [
+        "#000000", "#FFFFFF", "#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF",
+        "#000000", "#FFFFFF", "#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF",
+        "#800000", "#008000", "#000080", "#808000", "#800080", "#008080", "#C0C0C0", "#808080",
+        "#9999FF", "#993366", "#FFFFCC", "#CCFFFF", "#660066", "#FF8080", "#0066CC", "#CCCCFF",
+        "#000080", "#FF00FF", "#FFFF00", "#00FFFF", "#800080", "#800000", "#008080", "#0000FF",
+        "#00CCFF", "#CCFFFF", "#CCFFCC", "#FFFF99", "#99CCFF", "#FF99CC", "#CC99FF", "#FFCC99",
+        "#3366FF", "#33CCCC", "#99CC00", "#FFCC00", "#FF9900", "#FF6600", "#666699", "#969696",
+        "#003366", "#339966", "#003300", "#333300", "#993300", "#993366", "#333399",
+        "#333333",
+        // 64, Transparent)
+    ];
+    if index > 63 {
+        return color_list[0].to_string();
+    }
+    color_list[index as usize].to_string()
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::import::colors::*;
+
+    #[test]
+    fn test_known_colors() {
+        let color1 = get_themed_color(0, -0.05);
+        assert_eq!(color1, "#F2F2F2");
+
+        let color2 = get_themed_color(5, -0.25);
+        // Excel returns "#C65911" (rounding error)
+        assert_eq!(color2, "#C55911");
+
+        let color3 = get_themed_color(4, 0.6);
+        // Excel returns "#b4c6e7" (rounding error)
+        assert_eq!(color3, "#B5C8E8");
+    }
+
+    #[test]
+    fn test_rgb_hex() {
+        struct ColorTest {
+            hex: String,
+            rgb: [i32; 3],
+            hsl: [i32; 3],
+        }
+        let color_tests = [
+            ColorTest {
+                hex: "#FFFFFF".to_string(),
+                rgb: [255, 255, 255],
+                hsl: [0, 0, 100],
+            },
+            ColorTest {
+                hex: "#000000".to_string(),
+                rgb: [0, 0, 0],
+                hsl: [0, 0, 0],
+            },
+            ColorTest {
+                hex: "#44546A".to_string(),
+                rgb: [68, 84, 106],
+                hsl: [215, 22, 34],
+            },
+            ColorTest {
+                hex: "#E7E6E6".to_string(),
+                rgb: [231, 230, 230],
+                hsl: [0, 2, 90],
+            },
+            ColorTest {
+                hex: "#4472C4".to_string(),
+                rgb: [68, 114, 196],
+                hsl: [218, 52, 52],
+            },
+            ColorTest {
+                hex: "#ED7D31".to_string(),
+                rgb: [237, 125, 49],
+                hsl: [24, 84, 56],
+            },
+            ColorTest {
+                hex: "#A5A5A5".to_string(),
+                rgb: [165, 165, 165],
+                hsl: [0, 0, 65],
+            },
+            ColorTest {
+                hex: "#FFC000".to_string(),
+                rgb: [255, 192, 0],
+                hsl: [45, 100, 50],
+            },
+            ColorTest {
+                hex: "#5B9BD5".to_string(),
+                rgb: [91, 155, 213],
+                hsl: [209, 59, 60],
+            },
+            ColorTest {
+                hex: "#70AD47".to_string(),
+                rgb: [112, 173, 71],
+                hsl: [96, 42, 48],
+            },
+            ColorTest {
+                hex: "#0563C1".to_string(),
+                rgb: [5, 99, 193],
+                hsl: [210, 95, 39],
+            },
+            ColorTest {
+                hex: "#954F72".to_string(),
+                rgb: [149, 79, 114],
+                hsl: [330, 31, 45],
+            },
+        ];
+        for color in color_tests.iter() {
+            let rgb = color.rgb;
+            let hsl = color.hsl;
+            assert_eq!(rgb, hex_to_rgb(&color.hex));
+            assert_eq!(hsl, rgb_to_hsl(rgb));
+            assert_eq!(rgb_to_hex(rgb), color.hex);
+            // The round trip has rounding errors
+            // FIXME: We could also hardcode the hsl21 in the testcase
+            let rgb2 = hsl_to_rgb(hsl);
+            let diff =
+                (rgb2[0] - rgb[0]).abs() + (rgb2[1] - rgb[1]).abs() + (rgb2[2] - rgb[2]).abs();
+            assert!(diff < 4);
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc/import/metadata.rs.html b/src/ironcalc/import/metadata.rs.html new file mode 100644 index 0000000..a03dd34 --- /dev/null +++ b/src/ironcalc/import/metadata.rs.html @@ -0,0 +1,163 @@ +metadata.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+
use std::io::Read;
+
+use ironcalc_base::types::Metadata;
+
+use crate::error::XlsxError;
+
+use super::util::get_value_or_default;
+
+struct AppData {
+    application: String,
+    app_version: String,
+}
+
+struct CoreData {
+    creator: String,
+    last_modified_by: String,
+    created: String,
+    last_modified: String,
+}
+
+fn load_core<R: Read + std::io::Seek>(
+    archive: &mut zip::read::ZipArchive<R>,
+) -> Result<CoreData, XlsxError> {
+    let mut file = archive.by_name("docProps/core.xml")?;
+    let mut text = String::new();
+    file.read_to_string(&mut text)?;
+    let doc = roxmltree::Document::parse(&text)?;
+    let core_data = doc
+        .root()
+        .first_child()
+        .ok_or_else(|| XlsxError::Xml("Corrupt XML structure".to_string()))?;
+    // Note the namespace should be "http://purl.org/dc/elements/1.1/"
+    let creator = get_value_or_default(&core_data, "creator", "Anonymous User");
+    // Note namespace is "http://schemas.openxmlformats.org/package/2006/metadata/core-properties"
+    let last_modified_by = get_value_or_default(&core_data, "lastModifiedBy", "Anonymous User");
+    // In these two cases the namespace is "http://purl.org/dc/terms/"
+    let created = get_value_or_default(&core_data, "created", "");
+    let last_modified = get_value_or_default(&core_data, "modified", "");
+
+    Ok(CoreData {
+        creator,
+        last_modified_by,
+        created,
+        last_modified,
+    })
+}
+
+fn load_app<R: Read + std::io::Seek>(
+    archive: &mut zip::read::ZipArchive<R>,
+) -> Result<AppData, XlsxError> {
+    let mut file = archive.by_name("docProps/app.xml")?;
+    let mut text = String::new();
+    file.read_to_string(&mut text)?;
+    let doc = roxmltree::Document::parse(&text)?;
+    let app_data = doc
+        .root()
+        .first_child()
+        .ok_or_else(|| XlsxError::Xml("Corrupt XML structure".to_string()))?;
+
+    let application = get_value_or_default(&app_data, "Application", "Unknown application");
+    let app_version = get_value_or_default(&app_data, "AppVersion", "");
+    Ok(AppData {
+        application,
+        app_version,
+    })
+}
+
+pub(super) fn load_metadata<R: Read + std::io::Seek>(
+    archive: &mut zip::read::ZipArchive<R>,
+) -> Result<Metadata, XlsxError> {
+    let app_data = load_app(archive)?;
+    let core_data = load_core(archive)?;
+    Ok(Metadata {
+        application: app_data.application,
+        app_version: app_data.app_version,
+        creator: core_data.creator,
+        last_modified_by: core_data.last_modified_by,
+        created: core_data.created,
+        last_modified: core_data.last_modified,
+    })
+}
+
\ No newline at end of file diff --git a/src/ironcalc/import/mod.rs.html b/src/ironcalc/import/mod.rs.html new file mode 100644 index 0000000..54c5da4 --- /dev/null +++ b/src/ironcalc/import/mod.rs.html @@ -0,0 +1,249 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+
mod colors;
+mod metadata;
+mod shared_strings;
+mod styles;
+mod tables;
+mod util;
+mod workbook;
+mod worksheets;
+
+use std::{
+    collections::HashMap,
+    fs,
+    io::{BufReader, Read},
+};
+
+use roxmltree::Node;
+
+use ironcalc_base::{
+    model::Model,
+    types::{Metadata, Workbook, WorkbookSettings},
+};
+
+use crate::error::XlsxError;
+
+use shared_strings::read_shared_strings;
+
+use metadata::load_metadata;
+use styles::load_styles;
+use util::get_attribute;
+use workbook::load_workbook;
+use worksheets::{load_sheets, Relationship};
+
+fn load_relationships<R: Read + std::io::Seek>(
+    archive: &mut zip::ZipArchive<R>,
+) -> Result<HashMap<String, Relationship>, XlsxError> {
+    let mut file = archive.by_name("xl/_rels/workbook.xml.rels")?;
+    let mut text = String::new();
+    file.read_to_string(&mut text)?;
+    let doc = roxmltree::Document::parse(&text)?;
+    let nodes: Vec<Node> = doc
+        .descendants()
+        .filter(|n| n.has_tag_name("Relationship"))
+        .collect();
+    let mut rels = HashMap::new();
+    for node in nodes {
+        rels.insert(
+            get_attribute(&node, "Id")?.to_string(),
+            Relationship {
+                rel_type: get_attribute(&node, "Type")?.to_string(),
+                target: get_attribute(&node, "Target")?.to_string(),
+            },
+        );
+    }
+    Ok(rels)
+}
+
+fn load_xlsx_from_reader<R: Read + std::io::Seek>(
+    name: String,
+    reader: R,
+    locale: &str,
+    tz: &str,
+) -> Result<Workbook, XlsxError> {
+    let mut archive = zip::ZipArchive::new(reader)?;
+
+    let mut shared_strings = read_shared_strings(&mut archive)?;
+    let workbook = load_workbook(&mut archive)?;
+    let rels = load_relationships(&mut archive)?;
+    let mut tables = HashMap::new();
+    let worksheets = load_sheets(
+        &mut archive,
+        &rels,
+        &workbook,
+        &mut tables,
+        &mut shared_strings,
+    )?;
+    let styles = load_styles(&mut archive)?;
+    let metadata = match load_metadata(&mut archive) {
+        Ok(metadata) => metadata,
+        Err(_) => {
+            // In case there is no metadata, add some
+            Metadata {
+                application: "Unknown application".to_string(),
+                app_version: "".to_string(),
+                creator: "".to_string(),
+                last_modified_by: "".to_string(),
+                created: "".to_string(),
+                last_modified: "".to_string(),
+            }
+        }
+    };
+    Ok(Workbook {
+        shared_strings,
+        defined_names: workbook.defined_names,
+        worksheets,
+        styles,
+        name,
+        settings: WorkbookSettings {
+            tz: tz.to_string(),
+            locale: locale.to_string(),
+        },
+        metadata,
+        tables,
+    })
+}
+
+// Public methods
+
+/// Imports a file from disk into an internal representation
+pub fn load_from_excel(file_name: &str, locale: &str, tz: &str) -> Result<Workbook, XlsxError> {
+    let file_path = std::path::Path::new(file_name);
+    let file = fs::File::open(file_path)?;
+    let reader = BufReader::new(file);
+    let name = file_path
+        .file_stem()
+        .ok_or_else(|| XlsxError::IO("Could not extract workbook name".to_string()))?
+        .to_string_lossy()
+        .to_string();
+    load_xlsx_from_reader(name, reader, locale, tz)
+}
+
+pub fn load_model_from_xlsx(file_name: &str, locale: &str, tz: &str) -> Result<Model, XlsxError> {
+    let workbook = load_from_excel(file_name, locale, tz)?;
+    Model::from_workbook(workbook).map_err(XlsxError::Workbook)
+}
+
\ No newline at end of file diff --git a/src/ironcalc/import/shared_strings.rs.html b/src/ironcalc/import/shared_strings.rs.html new file mode 100644 index 0000000..3202487 --- /dev/null +++ b/src/ironcalc/import/shared_strings.rs.html @@ -0,0 +1,161 @@ +shared_strings.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+
use std::io::Read;
+
+use roxmltree::Node;
+
+use crate::error::XlsxError;
+
+/// Reads the list of shared strings in an Excel workbook
+/// Note than in IronCalc we lose _internal_ styling of a string
+/// See Section 18.4
+pub(crate) fn read_shared_strings<R: Read + std::io::Seek>(
+    archive: &mut zip::read::ZipArchive<R>,
+) -> Result<Vec<String>, XlsxError> {
+    match archive.by_name("xl/sharedStrings.xml") {
+        Ok(mut file) => {
+            let mut text = String::new();
+            file.read_to_string(&mut text)?;
+            read_shared_strings_from_string(&text)
+        }
+        Err(_e) => Ok(Vec::new()),
+    }
+}
+
+fn read_shared_strings_from_string(text: &str) -> Result<Vec<String>, XlsxError> {
+    let doc = roxmltree::Document::parse(text)?;
+    let mut shared_strings = Vec::new();
+    let nodes: Vec<Node> = doc.descendants().filter(|n| n.has_tag_name("si")).collect();
+    for node in nodes {
+        let text = node
+            .descendants()
+            .filter(|n| n.has_tag_name("t"))
+            .map(|n| n.text().unwrap_or("").to_string())
+            .collect::<Vec<String>>()
+            .join("");
+        shared_strings.push(text);
+    }
+    Ok(shared_strings)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_shared_strings() {
+        let xml_string = r#"
+<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="3" uniqueCount="3">
+    <si>
+        <t>A string</t>
+    </si>
+    <si>
+        <t>A second String</t>
+    </si>
+    <si>
+        <r>
+            <t>Hello</t>
+        </r>
+            <r>
+            <rPr>
+                <b/>
+                <sz val="11"/>
+                <color rgb="FFFF0000"/>
+                <rFont val="Calibri"/>
+                <family val="2"/>
+                <scheme val="minor"/>
+            </rPr>
+            <t xml:space="preserve"> World</t>
+        </r>
+    </si>
+</sst>"#;
+        let shared_strings = read_shared_strings_from_string(xml_string.trim()).unwrap();
+        assert_eq!(
+            shared_strings,
+            [
+                "A string".to_string(),
+                "A second String".to_string(),
+                "Hello World".to_string()
+            ]
+        );
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc/import/styles.rs.html b/src/ironcalc/import/styles.rs.html new file mode 100644 index 0000000..1735669 --- /dev/null +++ b/src/ironcalc/import/styles.rs.html @@ -0,0 +1,773 @@ +styles.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+
use std::{collections::HashMap, io::Read};
+
+use ironcalc_base::types::{
+    Alignment, Border, BorderItem, BorderStyle, CellStyleXfs, CellStyles, CellXfs, Fill, Font,
+    FontScheme, HorizontalAlignment, NumFmt, Styles, VerticalAlignment,
+};
+use roxmltree::Node;
+
+use crate::error::XlsxError;
+
+use super::util::{get_attribute, get_bool, get_bool_false, get_color, get_number};
+
+fn get_border(node: Node, name: &str) -> Result<Option<BorderItem>, XlsxError> {
+    let style;
+    let color;
+    let border_nodes = node
+        .children()
+        .filter(|n| n.has_tag_name(name))
+        .collect::<Vec<Node>>();
+    if border_nodes.len() == 1 {
+        let border = border_nodes[0];
+        style = match border.attribute("style") {
+            Some("thin") => BorderStyle::Thin,
+            Some("medium") => BorderStyle::Medium,
+            Some("thick") => BorderStyle::Thick,
+            Some("double") => BorderStyle::Double,
+            Some("slantdashdot") => BorderStyle::SlantDashDot,
+            Some("mediumdashed") => BorderStyle::MediumDashed,
+            Some("mediumdashdot") => BorderStyle::MediumDashDot,
+            Some("mediumdashdotdot") => BorderStyle::MediumDashDotDot,
+            // TODO: Should we fail in this case or set the border to None?
+            Some(_) => BorderStyle::Thin,
+            None => {
+                return Ok(None);
+            }
+        };
+
+        let color_node = border
+            .children()
+            .filter(|n| n.has_tag_name("color"))
+            .collect::<Vec<Node>>();
+        if color_node.len() == 1 {
+            color = get_color(color_node[0])?;
+        } else {
+            color = None;
+        }
+    } else {
+        return Ok(None);
+    }
+    Ok(Some(BorderItem { style, color }))
+}
+
+pub(super) fn load_styles<R: Read + std::io::Seek>(
+    archive: &mut zip::read::ZipArchive<R>,
+) -> Result<Styles, XlsxError> {
+    let mut file = archive.by_name("xl/styles.xml")?;
+    let mut text = String::new();
+    file.read_to_string(&mut text)?;
+    let doc = roxmltree::Document::parse(&text)?;
+    let style_sheet = doc
+        .root()
+        .first_child()
+        .ok_or_else(|| XlsxError::Xml("Corrupt XML structure".to_string()))?;
+
+    let mut num_fmts = Vec::new();
+    let num_fmts_nodes = style_sheet
+        .children()
+        .filter(|n| n.has_tag_name("numFmts"))
+        .collect::<Vec<Node>>();
+    if num_fmts_nodes.len() == 1 {
+        for num_fmt in num_fmts_nodes[0].children() {
+            let num_fmt_id = get_number(num_fmt, "numFmtId");
+            let format_code = num_fmt.attribute("formatCode").unwrap_or("").to_string();
+            num_fmts.push(NumFmt {
+                num_fmt_id,
+                format_code,
+            });
+        }
+    }
+
+    let mut fonts = Vec::new();
+    let font_nodes = style_sheet
+        .children()
+        .filter(|n| n.has_tag_name("fonts"))
+        .collect::<Vec<Node>>()[0];
+    for font in font_nodes.children() {
+        let mut sz = 11;
+        let mut name = "Calibri".to_string();
+        // NOTE: In Excel you can have simple underline or double underline
+        // In IronCalc convert double underline to simple
+        // This in excel is u with a value of "double"
+        let mut u = false;
+        let mut b = false;
+        let mut i = false;
+        let mut strike = false;
+        let mut color = Some("FFFFFF00".to_string());
+        let mut family = 2;
+        let mut scheme = FontScheme::default();
+        for feature in font.children() {
+            match feature.tag_name().name() {
+                "sz" => {
+                    sz = feature
+                        .attribute("val")
+                        .unwrap_or("11")
+                        .parse::<i32>()
+                        .unwrap_or(11);
+                }
+                "color" => {
+                    color = get_color(feature)?;
+                }
+                "u" => {
+                    u = true;
+                }
+                "b" => {
+                    b = true;
+                }
+                "i" => {
+                    i = true;
+                }
+                "strike" => {
+                    strike = true;
+                }
+                "name" => name = feature.attribute("val").unwrap_or("Calibri").to_string(),
+                // If there is a theme the font scheme and family overrides other properties like the name
+                "family" => {
+                    family = feature
+                        .attribute("val")
+                        .unwrap_or("2")
+                        .parse::<i32>()
+                        .unwrap_or(2);
+                }
+                "scheme" => {
+                    scheme = match feature.attribute("val") {
+                        None => FontScheme::default(),
+                        Some("minor") => FontScheme::Minor,
+                        Some("major") => FontScheme::Major,
+                        Some("none") => FontScheme::None,
+                        // TODO: Should we fail?
+                        Some(_) => FontScheme::default(),
+                    }
+                }
+                "charset" => {}
+                _ => {
+                    println!("Unexpected feature {:?}", feature);
+                }
+            }
+        }
+        fonts.push(Font {
+            strike,
+            u,
+            b,
+            i,
+            sz,
+            color,
+            name,
+            family,
+            scheme,
+        });
+    }
+
+    let mut fills = Vec::new();
+    let fill_nodes = style_sheet
+        .children()
+        .filter(|n| n.has_tag_name("fills"))
+        .collect::<Vec<Node>>()[0];
+    for fill in fill_nodes.children() {
+        let pattern_fill = fill
+            .children()
+            .filter(|n| n.has_tag_name("patternFill"))
+            .collect::<Vec<Node>>();
+        if pattern_fill.len() != 1 {
+            // safety belt
+            // Some fills do not have a patternFill, but they have gradientFill
+            fills.push(Fill {
+                pattern_type: "solid".to_string(),
+                fg_color: None,
+                bg_color: None,
+            });
+            continue;
+        }
+        let pattern_fill = pattern_fill[0];
+
+        let pattern_type = pattern_fill
+            .attribute("patternType")
+            .unwrap_or("none")
+            .to_string();
+        let mut fg_color = None;
+        let mut bg_color = None;
+        for feature in pattern_fill.children() {
+            match feature.tag_name().name() {
+                "fgColor" => {
+                    fg_color = get_color(feature)?;
+                }
+                "bgColor" => {
+                    bg_color = get_color(feature)?;
+                }
+                _ => {
+                    println!("Unexpected pattern");
+                    dbg!(feature);
+                }
+            }
+        }
+        fills.push(Fill {
+            pattern_type,
+            fg_color,
+            bg_color,
+        })
+    }
+
+    let mut borders = Vec::new();
+    let border_nodes = style_sheet
+        .children()
+        .filter(|n| n.has_tag_name("borders"))
+        .collect::<Vec<Node>>()[0];
+    for border in border_nodes.children() {
+        let diagonal_up = get_bool_false(border, "diagonal_up");
+        let diagonal_down = get_bool_false(border, "diagonal_down");
+        let left = get_border(border, "left")?;
+        let right = get_border(border, "right")?;
+        let top = get_border(border, "top")?;
+        let bottom = get_border(border, "bottom")?;
+        let diagonal = get_border(border, "diagonal")?;
+        borders.push(Border {
+            diagonal_up,
+            diagonal_down,
+            left,
+            right,
+            top,
+            bottom,
+            diagonal,
+        });
+    }
+
+    let mut cell_style_xfs = Vec::new();
+    let cell_style_xfs_nodes = style_sheet
+        .children()
+        .filter(|n| n.has_tag_name("cellStyleXfs"))
+        .collect::<Vec<Node>>()[0];
+    for xfs in cell_style_xfs_nodes.children() {
+        let num_fmt_id = get_number(xfs, "numFmtId");
+        let font_id = get_number(xfs, "fontId");
+        let fill_id = get_number(xfs, "fillId");
+        let border_id = get_number(xfs, "borderId");
+        let apply_number_format = get_bool(xfs, "applyNumberFormat");
+        let apply_border = get_bool(xfs, "applyBorder");
+        let apply_alignment = get_bool(xfs, "applyAlignment");
+        let apply_protection = get_bool(xfs, "applyProtection");
+        let apply_font = get_bool(xfs, "applyFont");
+        let apply_fill = get_bool(xfs, "applyFill");
+
+        cell_style_xfs.push(CellStyleXfs {
+            num_fmt_id,
+            font_id,
+            fill_id,
+            border_id,
+            apply_number_format,
+            apply_border,
+            apply_alignment,
+            apply_protection,
+            apply_font,
+            apply_fill,
+        });
+    }
+
+    let mut cell_styles = Vec::new();
+    let mut style_names = HashMap::new();
+    let cell_style_nodes = style_sheet
+        .children()
+        .filter(|n| n.has_tag_name("cellStyles"))
+        .collect::<Vec<Node>>()[0];
+    for cell_style in cell_style_nodes.children() {
+        let name = get_attribute(&cell_style, "name")?.to_string();
+        let xf_id = get_number(cell_style, "xfId");
+        let builtin_id = get_number(cell_style, "builtinId");
+        style_names.insert(xf_id, name.clone());
+        cell_styles.push(CellStyles {
+            name,
+            xf_id,
+            builtin_id,
+        })
+    }
+
+    let mut cell_xfs = Vec::new();
+    let cell_xfs_nodes = style_sheet
+        .children()
+        .filter(|n| n.has_tag_name("cellXfs"))
+        .collect::<Vec<Node>>()[0];
+    for xfs in cell_xfs_nodes.children() {
+        let xf_id = get_attribute(&xfs, "xfId")?.parse::<i32>()?;
+        let num_fmt_id = get_number(xfs, "numFmtId");
+        let font_id = get_number(xfs, "fontId");
+        let fill_id = get_number(xfs, "fillId");
+        let border_id = get_number(xfs, "borderId");
+        let apply_number_format = get_bool_false(xfs, "applyNumberFormat");
+        let apply_border = get_bool_false(xfs, "applyBorder");
+        let apply_alignment = get_bool_false(xfs, "applyAlignment");
+        let apply_protection = get_bool_false(xfs, "applyProtection");
+        let apply_font = get_bool_false(xfs, "applyFont");
+        let apply_fill = get_bool_false(xfs, "applyFill");
+        let quote_prefix = get_bool_false(xfs, "quotePrefix");
+
+        // TODO: Pivot Tables
+        // let pivotButton = get_bool(xfs, "pivotButton");
+
+        let alignment_nodes = xfs
+            .children()
+            .filter(|n| n.has_tag_name("alignment"))
+            .collect::<Vec<Node>>();
+        let alignment = if alignment_nodes.len() == 1 {
+            let alignment_node = alignment_nodes[0];
+            let wrap_text = get_bool_false(alignment_node, "wrapText");
+
+            let horizontal = match alignment_node.attribute("horizontal") {
+                Some("center") => HorizontalAlignment::Center,
+                Some("centerContinuous") => HorizontalAlignment::CenterContinuous,
+                Some("distributed") => HorizontalAlignment::Distributed,
+                Some("fill") => HorizontalAlignment::Fill,
+                Some("general") => HorizontalAlignment::General,
+                Some("justify") => HorizontalAlignment::Justify,
+                Some("left") => HorizontalAlignment::Left,
+                Some("right") => HorizontalAlignment::Right,
+                // TODO: Should we fail in this case or set the alignment to default?
+                Some(_) => HorizontalAlignment::default(),
+                None => HorizontalAlignment::default(),
+            };
+
+            let vertical = match alignment_node.attribute("vertical") {
+                Some("bottom") => VerticalAlignment::Bottom,
+                Some("center") => VerticalAlignment::Center,
+                Some("distributed") => VerticalAlignment::Distributed,
+                Some("justify") => VerticalAlignment::Justify,
+                Some("top") => VerticalAlignment::Top,
+                // TODO: Should we fail in this case or set the alignment to default?
+                Some(_) => VerticalAlignment::default(),
+                None => VerticalAlignment::default(),
+            };
+
+            Some(Alignment {
+                horizontal,
+                vertical,
+                wrap_text,
+            })
+        } else {
+            None
+        };
+
+        cell_xfs.push(CellXfs {
+            xf_id,
+            num_fmt_id,
+            font_id,
+            fill_id,
+            border_id,
+            apply_number_format,
+            apply_border,
+            apply_alignment,
+            apply_protection,
+            apply_font,
+            apply_fill,
+            quote_prefix,
+            alignment,
+        });
+    }
+
+    // TODO
+    // let mut dxfs = Vec::new();
+    // let mut tableStyles = Vec::new();
+    // let mut colors = Vec::new();
+    // <colors>
+    //     <mruColors>
+    //         <color rgb="FFB1BB4D"/>
+    //         <color rgb="FFFF99CC"/>
+    //         <color rgb="FF6C56DC"/>
+    //         <color rgb="FFFF66CC"/>
+    //     </mruColors>
+    // </colors>
+
+    Ok(Styles {
+        num_fmts,
+        fonts,
+        fills,
+        borders,
+        cell_style_xfs,
+        cell_xfs,
+        cell_styles,
+    })
+}
+
\ No newline at end of file diff --git a/src/ironcalc/import/tables.rs.html b/src/ironcalc/import/tables.rs.html new file mode 100644 index 0000000..6b94431 --- /dev/null +++ b/src/ironcalc/import/tables.rs.html @@ -0,0 +1,431 @@ +tables.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+
use std::io::Read;
+
+use ironcalc_base::types::{Table, TableColumn, TableStyleInfo};
+use roxmltree::Node;
+
+use crate::error::XlsxError;
+
+use super::util::{get_bool, get_bool_false};
+
+// <table name="Table" displayName="Table" totalsRowCount ref="A1:D6">
+//   <autoFilter ref="A1:D6">
+//       <filterColumn colId="0">
+//            <customFilters><customFilter operator="greaterThan" val=20></customFilter></customFilters>
+//       </filterColumn>
+//   </autoFilter>
+//   <tableColumns count="5">
+//      <tableColumn name="Monday" totalsRowFunction="sum" />
+//      ...
+//   </tableColumns>
+//   <tableStyleInfo name="TableStyle5"/>
+// </table>
+
+/// Reads a table in an Excel workbook
+pub(crate) fn load_table<R: Read + std::io::Seek>(
+    archive: &mut zip::read::ZipArchive<R>,
+    path: &str,
+    sheet_name: &str,
+) -> Result<Table, XlsxError> {
+    let mut file = archive.by_name(path)?;
+    let mut text = String::new();
+    file.read_to_string(&mut text)?;
+    let document = roxmltree::Document::parse(&text)?;
+
+    // table
+    let table = document
+        .root()
+        .first_child()
+        .ok_or_else(|| XlsxError::Xml("Corrupt XML structure".to_string()))?;
+
+    // Name and display name are normally the same and are unique in a workbook
+    // They also need to be different from any defined name
+    let name = table
+        .attribute("name")
+        .expect("Missing table name")
+        .to_string();
+
+    let display_name = table
+        .attribute("name")
+        .expect("Missing table display name")
+        .to_string();
+
+    // Range of the table, including the totals if any and headers.
+    let reference = table
+        .attribute("ref")
+        .expect("Missing table ref")
+        .to_string();
+
+    // Either 0 or 1, indicates if the table has a formula for totals at the bottom of the table
+    let totals_row_count = match table.attribute("totalsRowCount") {
+        Some(s) => s.parse::<u32>().expect("Invalid totalsRowCount"),
+        None => 0,
+    };
+
+    // Either 0 or 1, indicates if the table has headers at the top of the table
+    let header_row_count = match table.attribute("headerRowCount") {
+        Some(s) => s.parse::<u32>().expect("Invalid headerRowCount"),
+        None => 1,
+    };
+
+    // style index of the header row of the table
+    let header_row_dxf_id = if let Some(index_str) = table.attribute("headerRowDxfId") {
+        match index_str.parse::<u32>() {
+            Ok(i) => Some(i),
+            Err(_) => None,
+        }
+    } else {
+        None
+    };
+
+    // style index of the header row of the table
+    let data_dxf_id = if let Some(index_str) = table.attribute("headerRowDxfId") {
+        match index_str.parse::<u32>() {
+            Ok(i) => Some(i),
+            Err(_) => None,
+        }
+    } else {
+        None
+    };
+
+    // style index of the totals row of the table
+    let totals_row_dxf_id = if let Some(index_str) = table.attribute("totalsRowDxfId") {
+        match index_str.parse::<u32>() {
+            Ok(i) => Some(i),
+            Err(_) => None,
+        }
+    } else {
+        None
+    };
+
+    // Missing in Calc: styles can also be defined via a name:
+    // headerRowCellStyle, dataCellStyle, totalsRowCellStyle
+
+    // Missing in Calc: styles can also be applied to the borders:
+    // headerRowBorderDxfId, tableBorderDxfId, totalsRowBorderDxfId
+
+    // TODO: Conformant implementations should panic if header_row_dxf_id or data_dxf_id are out of bounds.
+
+    // Note that filters are non dynamic
+    // The only thing important for us is whether or not it has filters
+    let auto_filter = table
+        .descendants()
+        .filter(|n| n.has_tag_name("autoFilter"))
+        .collect::<Vec<Node>>();
+
+    let has_filters = if let Some(filter) = auto_filter.get(0) {
+        filter.children().count() > 0
+    } else {
+        false
+    };
+
+    // tableColumn
+    let table_column = table
+        .descendants()
+        .filter(|n| n.has_tag_name("tableColumn"))
+        .collect::<Vec<Node>>();
+    let mut columns = Vec::new();
+    for table_column in table_column {
+        let column_name = table_column.attribute("name").expect("Missing column name");
+        let id = table_column.attribute("id").expect("Missing column id");
+        let id = id.parse::<u32>().expect("Invalid id");
+
+        // style index of the header row of the table
+        let header_row_dxf_id = if let Some(index_str) = table_column.attribute("headerRowDxfId") {
+            match index_str.parse::<u32>() {
+                Ok(i) => Some(i),
+                Err(_) => None,
+            }
+        } else {
+            None
+        };
+
+        // style index of the header row of the table column
+        let data_dxf_id = if let Some(index_str) = table_column.attribute("headerRowDxfId") {
+            match index_str.parse::<u32>() {
+                Ok(i) => Some(i),
+                Err(_) => None,
+            }
+        } else {
+            None
+        };
+
+        // style index of the totals row of the table column
+        let totals_row_dxf_id = if let Some(index_str) = table_column.attribute("totalsRowDxfId") {
+            match index_str.parse::<u32>() {
+                Ok(i) => Some(i),
+                Err(_) => None,
+            }
+        } else {
+            None
+        };
+
+        // NOTE: Same as before, we should panic if indices to differential formatting records are out of bounds
+        // Missing in Calc: styles can also be defined via a name:
+        // headerRowCellStyle, dataCellStyle, totalsRowCellStyle
+
+        columns.push(TableColumn {
+            id,
+            name: column_name.to_string(),
+            totals_row_label: None,
+            header_row_dxf_id,
+            data_dxf_id,
+            totals_row_function: None,
+            totals_row_dxf_id,
+        });
+    }
+
+    // tableInfo
+    let table_info = table
+        .descendants()
+        .filter(|n| n.has_tag_name("tableInfo"))
+        .collect::<Vec<Node>>();
+    let style_info = match table_info.get(0) {
+        Some(node) => {
+            let name = node.attribute("name").map(|s| s.to_string());
+            TableStyleInfo {
+                name,
+                show_first_column: get_bool_false(*node, "showFirstColumn"),
+                show_last_column: get_bool_false(*node, "showLastColumn"),
+                show_row_stripes: get_bool(*node, "showRowStripes"),
+                show_column_stripes: get_bool_false(*node, "showColumnStripes"),
+            }
+        }
+        None => TableStyleInfo {
+            name: None,
+            show_first_column: false,
+            show_last_column: false,
+            show_row_stripes: true,
+            show_column_stripes: false,
+        },
+    };
+    Ok(Table {
+        name,
+        display_name,
+        reference,
+        totals_row_count,
+        header_row_count,
+        header_row_dxf_id,
+        data_dxf_id,
+        totals_row_dxf_id,
+        columns,
+        style_info,
+        has_filters,
+        sheet_name: sheet_name.to_string(),
+    })
+}
+
\ No newline at end of file diff --git a/src/ironcalc/import/util.rs.html b/src/ironcalc/import/util.rs.html new file mode 100644 index 0000000..a4f555d --- /dev/null +++ b/src/ironcalc/import/util.rs.html @@ -0,0 +1,157 @@ +util.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+
use colors::{get_indexed_color, get_themed_color};
+use roxmltree::{ExpandedName, Node};
+
+use crate::error::XlsxError;
+
+use super::colors;
+
+pub(crate) fn get_number(node: Node, s: &str) -> i32 {
+    node.attribute(s).unwrap_or("0").parse::<i32>().unwrap_or(0)
+}
+
+#[inline]
+pub(super) fn get_attribute<'a, 'n, 'm, N>(
+    node: &'a Node,
+    attr_name: N,
+) -> Result<&'a str, XlsxError>
+where
+    N: Into<ExpandedName<'n, 'm>>,
+{
+    let attr_name = attr_name.into();
+    node.attribute(attr_name)
+        .ok_or_else(|| XlsxError::Xml(format!("Missing \"{:?}\" XML attribute", attr_name)))
+}
+
+pub(super) fn get_value_or_default(node: &Node, tag_name: &str, default: &str) -> String {
+    let application_nodes = node
+        .children()
+        .filter(|n| n.has_tag_name(tag_name))
+        .collect::<Vec<Node>>();
+    if application_nodes.len() == 1 {
+        application_nodes[0].text().unwrap_or(default).to_string()
+    } else {
+        default.to_string()
+    }
+}
+
+pub(super) fn get_color(node: Node) -> Result<Option<String>, XlsxError> {
+    // 18.3.1.15 color (Data Bar Color)
+    if node.has_attribute("rgb") {
+        let mut val = node.attribute("rgb").unwrap().to_string();
+        // FIXME the two first values is normally the alpha.
+        if val.len() == 8 {
+            val = format!("#{}", &val[2..8]);
+        }
+        Ok(Some(val))
+    } else if node.has_attribute("indexed") {
+        let index = node.attribute("indexed").unwrap().parse::<i32>()?;
+        let rgb = get_indexed_color(index);
+        Ok(Some(rgb))
+    // Color::Indexed(val)
+    } else if node.has_attribute("theme") {
+        let theme = node.attribute("theme").unwrap().parse::<i32>()?;
+        let tint = match node.attribute("tint") {
+            Some(t) => t.parse::<f64>().unwrap_or(0.0),
+            None => 0.0,
+        };
+        let rgb = get_themed_color(theme, tint);
+        Ok(Some(rgb))
+    // Color::Theme { theme, tint }
+    } else if node.has_attribute("auto") {
+        // TODO: Is this correct?
+        // A boolean value indicating the color is automatic and system color dependent.
+        Ok(None)
+    } else {
+        println!("Unexpected color node {:?}", node);
+        Ok(None)
+    }
+}
+
+pub(super) fn get_bool(node: Node, s: &str) -> bool {
+    // defaults to true
+    !matches!(node.attribute(s), Some("0"))
+}
+
+pub(super) fn get_bool_false(node: Node, s: &str) -> bool {
+    // defaults to false
+    matches!(node.attribute(s), Some("1"))
+}
+
\ No newline at end of file diff --git a/src/ironcalc/import/workbook.rs.html b/src/ironcalc/import/workbook.rs.html new file mode 100644 index 0000000..2cf1f4e --- /dev/null +++ b/src/ironcalc/import/workbook.rs.html @@ -0,0 +1,159 @@ +workbook.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+
use std::io::Read;
+
+use ironcalc_base::types::{DefinedName, SheetState};
+use roxmltree::Node;
+
+use crate::error::XlsxError;
+
+use super::{
+    util::get_attribute,
+    worksheets::{Sheet, WorkbookXML},
+};
+
+pub(super) fn load_workbook<R: Read + std::io::Seek>(
+    archive: &mut zip::read::ZipArchive<R>,
+) -> Result<WorkbookXML, XlsxError> {
+    let mut file = archive.by_name("xl/workbook.xml")?;
+    let mut text = String::new();
+    file.read_to_string(&mut text)?;
+    let doc = roxmltree::Document::parse(&text)?;
+    let mut defined_names = Vec::new();
+    let mut sheets = Vec::new();
+    // Get the sheets
+    let sheet_nodes: Vec<Node> = doc
+        .descendants()
+        .filter(|n| n.has_tag_name("sheet"))
+        .collect();
+    for sheet in sheet_nodes {
+        let name = get_attribute(&sheet, "name")?.to_string();
+        let sheet_id = get_attribute(&sheet, "sheetId")?.to_string();
+        let sheet_id = sheet_id.parse::<u32>()?;
+        let id = get_attribute(
+            &sheet,
+            (
+                "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
+                "id",
+            ),
+        )?
+        .to_string();
+        let state = match sheet.attribute("state") {
+            Some("visible") | None => SheetState::Visible,
+            Some("hidden") => SheetState::Hidden,
+            Some("veryHidden") => SheetState::VeryHidden,
+            Some(state) => return Err(XlsxError::Xml(format!("Unknown sheet state: {}", state))),
+        };
+        sheets.push(Sheet {
+            name,
+            sheet_id,
+            id,
+            state,
+        });
+    }
+    // Get the defined names
+    let name_nodes: Vec<Node> = doc
+        .descendants()
+        .filter(|n| n.has_tag_name("definedName"))
+        .collect();
+    for node in name_nodes {
+        let name = get_attribute(&node, "name")?.to_string();
+        let formula = node.text().unwrap_or("").to_string();
+        // NOTE: In Excel the `localSheetId` is just the index of the worksheet and unrelated to the sheetId
+        let sheet_id = match node.attribute("localSheetId") {
+            Some(s) => {
+                let index = s.parse::<usize>()?;
+                Some(sheets[index].sheet_id)
+            }
+            None => None,
+        };
+        defined_names.push(DefinedName {
+            name,
+            formula,
+            sheet_id,
+        })
+    }
+    // read the relationships file
+    Ok(WorkbookXML {
+        worksheets: sheets,
+        defined_names,
+    })
+}
+
\ No newline at end of file diff --git a/src/ironcalc/import/worksheets.rs.html b/src/ironcalc/import/worksheets.rs.html new file mode 100644 index 0000000..00e6ca7 --- /dev/null +++ b/src/ironcalc/import/worksheets.rs.html @@ -0,0 +1,1851 @@ +worksheets.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+
use std::{collections::HashMap, io::Read, num::ParseIntError};
+
+use ironcalc_base::{
+    expressions::{
+        parser::{stringify::to_rc_format, Parser},
+        token::{get_error_by_english_name, Error},
+        types::CellReferenceRC,
+        utils::column_to_number,
+    },
+    types::{Cell, Col, Comment, DefinedName, Row, SheetData, SheetState, Table, Worksheet},
+};
+use roxmltree::Node;
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+
+use crate::error::XlsxError;
+
+use super::{
+    tables::load_table,
+    util::{get_attribute, get_color, get_number},
+};
+
+#[derive(Serialize, Deserialize, Debug)]
+pub(crate) struct Sheet {
+    pub(crate) name: String,
+    pub(crate) sheet_id: u32,
+    pub(crate) id: String,
+    pub(crate) state: SheetState,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub(crate) struct WorkbookXML {
+    pub(crate) worksheets: Vec<Sheet>,
+    pub(crate) defined_names: Vec<DefinedName>,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub(crate) struct Relationship {
+    pub(crate) target: String,
+    pub(crate) rel_type: String,
+}
+
+fn get_column_from_ref(s: &str) -> String {
+    let cs = s.chars();
+    let mut column = Vec::<char>::new();
+    for c in cs {
+        if !c.is_ascii_digit() {
+            column.push(c);
+        }
+    }
+    column.into_iter().collect()
+}
+
+fn load_dimension(ws: Node) -> String {
+    // <dimension ref="A1:O18"/>
+    let application_nodes = ws
+        .children()
+        .filter(|n| n.has_tag_name("dimension"))
+        .collect::<Vec<Node>>();
+    if application_nodes.len() == 1 {
+        application_nodes[0]
+            .attribute("ref")
+            .unwrap_or("A1")
+            .to_string()
+    } else {
+        "A1".to_string()
+    }
+}
+
+fn load_columns(ws: Node) -> Result<Vec<Col>, XlsxError> {
+    // cols
+    // <cols>
+    //     <col min="5" max="5" width="38.26953125" customWidth="1"/>
+    //     <col min="6" max="6" width="9.1796875" style="1"/>
+    //     <col min="8" max="8" width="4" customWidth="1"/>
+    // </cols>
+    let mut cols = Vec::new();
+    let columns = ws
+        .children()
+        .filter(|n| n.has_tag_name("cols"))
+        .collect::<Vec<Node>>();
+    if columns.len() == 1 {
+        for col in columns[0].children() {
+            let min = get_attribute(&col, "min")?;
+            let min = min.parse::<i32>()?;
+            let max = get_attribute(&col, "max")?;
+            let max = max.parse::<i32>()?;
+            let width = get_attribute(&col, "width")?;
+            let width = width.parse::<f64>()?;
+            let custom_width = matches!(col.attribute("customWidth"), Some("1"));
+            let style = col
+                .attribute("style")
+                .map(|s| s.parse::<i32>().unwrap_or(0));
+            cols.push(Col {
+                min,
+                max,
+                width,
+                custom_width,
+                style,
+            })
+        }
+    }
+    Ok(cols)
+}
+
+fn load_merge_cells(ws: Node) -> Result<Vec<String>, XlsxError> {
+    // 18.3.1.55 Merge Cells
+    // <mergeCells count="1">
+    //    <mergeCell ref="K7:L10"/>
+    // </mergeCells>
+    let mut merge_cells = Vec::new();
+    let merge_cells_nodes = ws
+        .children()
+        .filter(|n| n.has_tag_name("mergeCells"))
+        .collect::<Vec<Node>>();
+    if merge_cells_nodes.len() == 1 {
+        for merge_cell in merge_cells_nodes[0].children() {
+            let reference = get_attribute(&merge_cell, "ref")?.to_string();
+            merge_cells.push(reference);
+        }
+    }
+    Ok(merge_cells)
+}
+
+fn load_sheet_color(ws: Node) -> Result<Option<String>, XlsxError> {
+    // <sheetPr>
+    //     <tabColor theme="5" tint="-0.249977111117893"/>
+    // </sheetPr>
+    let mut color = None;
+    let sheet_pr = ws
+        .children()
+        .filter(|n| n.has_tag_name("sheetPr"))
+        .collect::<Vec<Node>>();
+    if sheet_pr.len() == 1 {
+        let tabs = sheet_pr[0]
+            .children()
+            .filter(|n| n.has_tag_name("tabColor"))
+            .collect::<Vec<Node>>();
+        if tabs.len() == 1 {
+            color = get_color(tabs[0])?;
+        }
+    }
+    Ok(color)
+}
+
+fn load_comments<R: Read + std::io::Seek>(
+    archive: &mut zip::read::ZipArchive<R>,
+    path: &str,
+) -> Result<Vec<Comment>, XlsxError> {
+    let mut comments = Vec::new();
+    let mut file = archive.by_name(path)?;
+    let mut text = String::new();
+    file.read_to_string(&mut text)?;
+    let doc = roxmltree::Document::parse(&text)?;
+    let ws = doc
+        .root()
+        .first_child()
+        .ok_or_else(|| XlsxError::Xml("Corrupt XML structure".to_string()))?;
+    let comment_list = ws
+        .children()
+        .filter(|n| n.has_tag_name("commentList"))
+        .collect::<Vec<Node>>();
+    if comment_list.len() == 1 {
+        for comment in comment_list[0].children() {
+            let text = comment
+                .descendants()
+                .filter(|n| n.has_tag_name("t"))
+                .map(|n| n.text().unwrap().to_string())
+                .collect::<Vec<String>>()
+                .join("");
+            let cell_ref = get_attribute(&comment, "ref")?.to_string();
+            // TODO: Read author_name from the list of authors
+            let author_name = "".to_string();
+            comments.push(Comment {
+                text,
+                author_name,
+                author_id: None,
+                cell_ref,
+            });
+        }
+    }
+
+    Ok(comments)
+}
+
+#[derive(Error, Debug, PartialEq, Eq)]
+enum ParseReferenceError {
+    #[error("RowError: {0}")]
+    RowError(ParseIntError),
+    #[error("ColumnError: {0}")]
+    ColumnError(String),
+}
+
+// This parses Sheet1!AS23 into sheet, column and row
+// FIXME: This is buggy. Does not check that is a valid sheet name
+// There is a similar named function in ironcalc_base. We probably should fix both at the same time.
+// NB: Maybe use regexes for this?
+fn parse_reference(s: &str) -> Result<CellReferenceRC, ParseReferenceError> {
+    let bytes = s.as_bytes();
+    let mut sheet_name = "".to_string();
+    let mut column = "".to_string();
+    let mut row = "".to_string();
+    let mut state = "sheet"; // "sheet", "col", "row"
+    for &byte in bytes {
+        match state {
+            "sheet" => {
+                if byte == b'!' {
+                    state = "col"
+                } else {
+                    sheet_name.push(byte as char);
+                }
+            }
+            "col" => {
+                if byte.is_ascii_alphabetic() {
+                    column.push(byte as char);
+                } else {
+                    state = "row";
+                    row.push(byte as char);
+                }
+            }
+            _ => {
+                row.push(byte as char);
+            }
+        }
+    }
+    Ok(CellReferenceRC {
+        sheet: sheet_name,
+        row: row.parse::<i32>().map_err(ParseReferenceError::RowError)?,
+        column: column_to_number(&column).map_err(ParseReferenceError::ColumnError)?,
+    })
+}
+
+fn from_a1_to_rc(
+    formula: String,
+    worksheets: &[String],
+    context: String,
+    tables: HashMap<String, Table>,
+) -> Result<String, XlsxError> {
+    let mut parser = Parser::new(worksheets.to_owned(), tables);
+    let cell_reference =
+        parse_reference(&context).map_err(|error| XlsxError::Xml(error.to_string()))?;
+    let t = parser.parse(&formula, &Some(cell_reference));
+    Ok(to_rc_format(&t))
+}
+
+fn get_formula_index(formula: &str, shared_formulas: &[String]) -> Option<i32> {
+    for (index, f) in shared_formulas.iter().enumerate() {
+        if f == formula {
+            return Some(index as i32);
+        }
+    }
+    None
+}
+
+// FIXME
+#[allow(clippy::too_many_arguments)]
+fn get_cell_from_excel(
+    cell_value: Option<&str>,
+    value_metadata: Option<&str>,
+    cell_type: &str,
+    cell_style: i32,
+    formula_index: i32,
+    sheet_name: &str,
+    cell_ref: &str,
+    shared_strings: &mut Vec<String>,
+) -> Cell {
+    // Possible cell types:
+    // 18.18.11 ST_CellType (Cell Type)
+    //   b (Boolean)
+    //   d (Date)
+    //   e (Error)
+    //   inlineStr (Inline String)
+    //   n (Number)
+    //   s (Shared String)
+    //   str (String)
+
+    if formula_index == -1 {
+        match cell_type {
+            "b" => Cell::BooleanCell {
+                v: cell_value == Some("1"),
+                s: cell_style,
+            },
+            "n" => Cell::NumberCell {
+                v: cell_value.unwrap_or("0").parse::<f64>().unwrap_or(0.0),
+                s: cell_style,
+            },
+            "e" => {
+                // For compatibility reasons Excel does not put the value #SPILL! but adds it as a metadata
+                // Older engines would just import #VALUE!
+                let mut error_name = cell_value.unwrap_or("#ERROR!");
+                if error_name == "#VALUE!" && value_metadata.is_some() {
+                    error_name = match value_metadata {
+                        Some("1") => "#CALC!",
+                        Some("2") => "#SPILL!",
+                        _ => error_name,
+                    }
+                }
+                Cell::ErrorCell {
+                    ei: get_error_by_english_name(error_name).unwrap_or(Error::ERROR),
+                    s: cell_style,
+                }
+            }
+            "s" => Cell::SharedString {
+                si: cell_value.unwrap_or("0").parse::<i32>().unwrap_or(0),
+                s: cell_style,
+            },
+            "str" => {
+                let s = cell_value.unwrap_or("");
+                let si = if let Some(i) = shared_strings.iter().position(|r| r == s) {
+                    i
+                } else {
+                    shared_strings.push(s.to_string());
+                    shared_strings.len() - 1
+                } as i32;
+
+                Cell::SharedString { si, s: cell_style }
+            }
+            "d" => {
+                // Not implemented
+                println!("Invalid type (d) in {}!{}", sheet_name, cell_ref);
+                Cell::ErrorCell {
+                    ei: Error::NIMPL,
+                    s: cell_style,
+                }
+            }
+            "inlineStr" => {
+                // Not implemented
+                println!("Invalid type (inlineStr) in {}!{}", sheet_name, cell_ref);
+                Cell::ErrorCell {
+                    ei: Error::NIMPL,
+                    s: cell_style,
+                }
+            }
+            "empty" => Cell::EmptyCell { s: cell_style },
+            _ => {
+                // error
+                println!(
+                    "Unexpected type ({}) in {}!{}",
+                    cell_type, sheet_name, cell_ref
+                );
+                Cell::ErrorCell {
+                    ei: Error::ERROR,
+                    s: cell_style,
+                }
+            }
+        }
+    } else {
+        match cell_type {
+            "b" => Cell::CellFormulaBoolean {
+                f: formula_index,
+                v: cell_value == Some("1"),
+                s: cell_style,
+            },
+            "n" => Cell::CellFormulaNumber {
+                f: formula_index,
+                v: cell_value.unwrap_or("0").parse::<f64>().unwrap_or(0.0),
+                s: cell_style,
+            },
+            "e" => {
+                // For compatibility reasons Excel does not put the value #SPILL! but adds it as a metadata
+                // Older engines would just import #VALUE!
+                let mut error_name = cell_value.unwrap_or("#ERROR!");
+                if error_name == "#VALUE!" && value_metadata.is_some() {
+                    error_name = match value_metadata {
+                        Some("1") => "#CALC!",
+                        Some("2") => "#SPILL!",
+                        _ => error_name,
+                    }
+                }
+                Cell::CellFormulaError {
+                    f: formula_index,
+                    ei: get_error_by_english_name(error_name).unwrap_or(Error::ERROR),
+                    s: cell_style,
+                    o: format!("{}!{}", sheet_name, cell_ref),
+                    m: cell_value.unwrap_or("#ERROR!").to_string(),
+                }
+            }
+            "s" => {
+                // Not implemented
+                let o = format!("{}!{}", sheet_name, cell_ref);
+                let m = Error::NIMPL.to_string();
+                println!("Invalid type (s) in {}!{}", sheet_name, cell_ref);
+                Cell::CellFormulaError {
+                    f: formula_index,
+                    ei: Error::NIMPL,
+                    s: cell_style,
+                    o,
+                    m,
+                }
+            }
+            "str" => {
+                // In Excel and in IronCalc all strings in cells result of a formula are *not* shared strings.
+                Cell::CellFormulaString {
+                    f: formula_index,
+                    v: cell_value.unwrap_or("").to_string(),
+                    s: cell_style,
+                }
+            }
+            "d" => {
+                // Not implemented
+                println!("Invalid type (d) in {}!{}", sheet_name, cell_ref);
+                let o = format!("{}!{}", sheet_name, cell_ref);
+                let m = Error::NIMPL.to_string();
+                Cell::CellFormulaError {
+                    f: formula_index,
+                    ei: Error::NIMPL,
+                    s: cell_style,
+                    o,
+                    m,
+                }
+            }
+            "inlineStr" => {
+                // Not implemented
+                let o = format!("{}!{}", sheet_name, cell_ref);
+                let m = Error::NIMPL.to_string();
+                println!("Invalid type (inlineStr) in {}!{}", sheet_name, cell_ref);
+                Cell::CellFormulaError {
+                    f: formula_index,
+                    ei: Error::NIMPL,
+                    s: cell_style,
+                    o,
+                    m,
+                }
+            }
+            _ => {
+                // error
+                println!(
+                    "Unexpected type ({}) in {}!{}",
+                    cell_type, sheet_name, cell_ref
+                );
+                let o = format!("{}!{}", sheet_name, cell_ref);
+                let m = Error::ERROR.to_string();
+                Cell::CellFormulaError {
+                    f: formula_index,
+                    ei: Error::ERROR,
+                    s: cell_style,
+                    o,
+                    m,
+                }
+            }
+        }
+    }
+}
+
+fn load_sheet_rels<R: Read + std::io::Seek>(
+    archive: &mut zip::read::ZipArchive<R>,
+    path: &str,
+    tables: &mut HashMap<String, Table>,
+    sheet_name: &str,
+) -> Result<Vec<Comment>, XlsxError> {
+    // ...xl/worksheets/sheet6.xml -> xl/worksheets/_rels/sheet6.xml.rels
+    let mut comments = Vec::new();
+    let v: Vec<&str> = path.split("/worksheets/").collect();
+    let mut path = v[0].to_string();
+    path.push_str("/worksheets/_rels/");
+    path.push_str(v[1]);
+    path.push_str(".rels");
+    let file = archive.by_name(&path);
+    if file.is_err() {
+        return Ok(comments);
+    }
+    let mut text = String::new();
+    file.unwrap().read_to_string(&mut text)?;
+    let doc = roxmltree::Document::parse(&text)?;
+
+    let rels = doc
+        .root()
+        .first_child()
+        .ok_or_else(|| XlsxError::Xml("Corrupt XML structure".to_string()))?
+        .children()
+        .collect::<Vec<Node>>();
+    for rel in rels {
+        let t = get_attribute(&rel, "Type")?.to_string();
+        if t.ends_with("comments") {
+            let mut target = get_attribute(&rel, "Target")?.to_string();
+            // Target="../comments1.xlsx"
+            target.replace_range(..2, v[0]);
+            comments = load_comments(archive, &target)?;
+        } else if t.ends_with("table") {
+            let mut target = get_attribute(&rel, "Target")?.to_string();
+
+            let path = if let Some(p) = target.strip_prefix('/') {
+                p.to_string()
+            } else {
+                // Target="../table1.xlsx"
+                target.replace_range(..2, v[0]);
+                target
+            };
+
+            let table = load_table(archive, &path, sheet_name)?;
+            tables.insert(table.name.clone(), table);
+        }
+    }
+    Ok(comments)
+}
+
+fn get_frozen_rows_and_columns(ws: Node) -> (i32, i32) {
+    // <sheetViews>
+    //   <sheetView workbookViewId="0">
+    //     <selection activeCell="E10" sqref="E10"/>
+    //   </sheetView>
+    // </sheetViews>
+    // <sheetFormatPr defaultRowHeight="14.5" x14ac:dyDescent="0.35"/>
+
+    // If we have frozen rows and columns:
+
+    // <sheetView tabSelected="1" workbookViewId="0">
+    //   <pane xSplit="3" ySplit="2" topLeftCell="D3" activePane="bottomRight" state="frozen"/>
+    //   <selection pane="topRight" activeCell="D1" sqref="D1"/>
+    //   <selection pane="bottomLeft" activeCell="A3" sqref="A3"/>
+    //   <selection pane="bottomRight" activeCell="K16" sqref="K16"/>
+    // </sheetView>
+
+    // 18.18.52 ST_Pane (Pane Types)
+    // bottomLeft, bottomRight, topLeft, topRight
+
+    // NB: bottomLeft is used when only rows are frozen, etc
+    // Calc ignores all those.
+
+    let mut frozen_rows = 0;
+    let mut frozen_columns = 0;
+
+    // In Calc there can only be one sheetView
+    let sheet_views = ws
+        .children()
+        .filter(|n| n.has_tag_name("sheetViews"))
+        .collect::<Vec<Node>>();
+
+    if sheet_views.len() != 1 {
+        return (0, 0);
+    }
+
+    let sheet_view = sheet_views[0]
+        .children()
+        .filter(|n| n.has_tag_name("sheetView"))
+        .collect::<Vec<Node>>();
+
+    if sheet_view.len() != 1 {
+        return (0, 0);
+    }
+
+    let pane = sheet_view[0]
+        .children()
+        .filter(|n| n.has_tag_name("pane"))
+        .collect::<Vec<Node>>();
+
+    // 18.18.53 ST_PaneState (Pane State)
+    // frozen, frozenSplit, split
+    if pane.len() == 1 && pane[0].attribute("state").unwrap_or("split") == "frozen" {
+        // TODO: Should we assert that topLeft is consistent?
+        // let top_left_cell = pane[0].attribute("topLeftCell").unwrap_or("A1").to_string();
+
+        frozen_columns = get_number(pane[0], "xSplit");
+        frozen_rows = get_number(pane[0], "ySplit");
+    }
+    (frozen_rows, frozen_columns)
+}
+
+pub(super) struct SheetSettings {
+    pub id: u32,
+    pub name: String,
+    pub state: SheetState,
+    pub comments: Vec<Comment>,
+}
+
+pub(super) fn load_sheet<R: Read + std::io::Seek>(
+    archive: &mut zip::read::ZipArchive<R>,
+    path: &str,
+    settings: SheetSettings,
+    worksheets: &[String],
+    tables: &HashMap<String, Table>,
+    shared_strings: &mut Vec<String>,
+) -> Result<Worksheet, XlsxError> {
+    let sheet_name = &settings.name;
+    let sheet_id = settings.id;
+    let state = &settings.state;
+
+    let mut file = archive.by_name(path)?;
+    let mut text = String::new();
+    file.read_to_string(&mut text)?;
+    let doc = roxmltree::Document::parse(&text)?;
+    let ws = doc
+        .root()
+        .first_child()
+        .ok_or_else(|| XlsxError::Xml("Corrupt XML structure".to_string()))?;
+    let mut shared_formulas = Vec::new();
+
+    let dimension = load_dimension(ws);
+
+    let (frozen_rows, frozen_columns) = get_frozen_rows_and_columns(ws);
+
+    let cols = load_columns(ws)?;
+    let color = load_sheet_color(ws)?;
+
+    // sheetData
+    // <row r="1" spans="1:15" x14ac:dyDescent="0.35">
+    //     <c r="A1" t="s">
+    //         <v>0</v>
+    //     </c>
+    //     <c r="D1">
+    //         <f>C1+1</f>
+    //     </c>
+    // </row>
+
+    // holds the row heights
+    let mut rows = Vec::new();
+    let mut sheet_data = SheetData::new();
+    let sheet_data_nodes = ws
+        .children()
+        .filter(|n| n.has_tag_name("sheetData"))
+        .collect::<Vec<Node>>()[0];
+
+    let default_row_height = 14.5;
+
+    // holds a map from the formula index in Excel to the index in IronCalc
+    let mut index_map = HashMap::new();
+    for row in sheet_data_nodes.children() {
+        // This is the row number 1-indexed
+        let row_index = get_attribute(&row, "r")?.parse::<i32>()?;
+        // `spans` is not used in IronCalc at the moment (it's an optimization)
+        // let spans = row.attribute("spans");
+        // This is the height of the row
+        let has_height_attribute;
+        let height = match row.attribute("ht") {
+            Some(s) => {
+                has_height_attribute = true;
+                s.parse::<f64>().unwrap_or(default_row_height)
+            }
+            None => {
+                has_height_attribute = false;
+                default_row_height
+            }
+        };
+        let custom_height = matches!(row.attribute("customHeight"), Some("1"));
+        // The height of the row is always the visible height of the row
+        // If custom_height is false that means the height was calculated automatically:
+        // for example because a cell has many lines or a larger font
+
+        let row_style = match row.attribute("s") {
+            Some(s) => s.parse::<i32>().unwrap_or(0),
+            None => 0,
+        };
+        let custom_format = matches!(row.attribute("customFormat"), Some("1"));
+        let hidden = matches!(row.attribute("hidden"), Some("1"));
+
+        if custom_height || custom_format || row_style != 0 || has_height_attribute || hidden {
+            rows.push(Row {
+                r: row_index,
+                height,
+                s: row_style,
+                custom_height,
+                custom_format,
+                hidden,
+            });
+        }
+
+        // Unused attributes:
+        // * thickBot, thickTop, ph, collapsed, outlineLevel
+
+        let mut data_row = HashMap::new();
+
+        // 18.3.1.4 c (Cell)
+        // Child Elements:
+        // * v: Cell value
+        // * is: Rich Text Inline (not used in IronCalc)
+        // * f: Formula
+        // Attributes:
+        // r: reference. A1 style
+        // s: style index
+        // t: cell type
+        // Unused attributes
+        // cm (cell metadata), ph (Show Phonetic), vm (value metadata)
+        for cell in row.children() {
+            let cell_ref = get_attribute(&cell, "r")?;
+            let column_letter = get_column_from_ref(cell_ref);
+            let column = column_to_number(column_letter.as_str()).map_err(XlsxError::Xml)?;
+
+            let value_metadata = cell.attribute("vm");
+
+            // We check the value "v" child.
+            let vs: Vec<Node> = cell.children().filter(|n| n.has_tag_name("v")).collect();
+            let cell_value = if vs.len() == 1 {
+                Some(vs[0].text().unwrap_or(""))
+            } else {
+                None
+            };
+
+            // type, the default type being "n" for number
+            // If the cell does not have a value is an empty cell
+            let cell_type = match cell.attribute("t") {
+                Some(t) => t,
+                None => {
+                    if cell_value.is_none() {
+                        "empty"
+                    } else {
+                        "n"
+                    }
+                }
+            };
+
+            // style index, the default style is 0
+            let cell_style = match cell.attribute("s") {
+                Some(s) => s.parse::<i32>().unwrap_or(0),
+                None => 0,
+            };
+
+            // Check for formula
+            // In Excel some formulas are shared and some are not, but in IronCalc all formulas are shared
+            // A cell with a "non-shared" formula is like:
+            // <c r="E3">
+            //   <f>C2+1</f>
+            //   <v>3</v>
+            // </c>
+            // A cell with a shared formula will be either a "mother" cell:
+            // <c r="D2">
+            //   <f t="shared" ref="D2:D3" si="0">C2+1</f>
+            //   <v>3</v>
+            // </c>
+            // Or a "daughter" cell:
+            // <c r="D3">
+            //   <f t="shared" si="0"/>
+            //   <v>4</v>
+            // </c>
+            // In IronCalc two cells have the same formula iff the R1C1 representation is the same
+            // TODO: This algorithm could end up with "repeated" shared formulas
+            //       We could solve that with a second transversal.
+            let fs: Vec<Node> = cell.children().filter(|n| n.has_tag_name("f")).collect();
+            let mut formula_index = -1;
+            if fs.len() == 1 {
+                // formula types:
+                // 18.18.6 ST_CellFormulaType (Formula Type)
+                // array (Array Formula) Formula is an array formula.
+                // dataTable (Table Formula) Formula is a data table formula.
+                // normal (Normal) Formula is a regular cell formula. (Default)
+                // shared (Shared Formula) Formula is part of a shared formula.
+                let formula_type = fs[0].attribute("t").unwrap_or("normal");
+                match formula_type {
+                    "shared" => {
+                        // We have a shared formula
+                        let si = get_attribute(&fs[0], "si")?;
+                        let si = si.parse::<i32>()?;
+                        match fs[0].attribute("ref") {
+                            Some(_) => {
+                                // It's the mother cell. We do not use the ref attribute in IronCalc
+                                let formula = fs[0].text().unwrap_or("").to_string();
+                                let context = format!("{}!{}", sheet_name, cell_ref);
+                                let formula =
+                                    from_a1_to_rc(formula, worksheets, context, tables.clone())?;
+                                match index_map.get(&si) {
+                                    Some(index) => {
+                                        // The index for that formula already exists meaning we bumped into a daughter cell first
+                                        // TODO: Worth assert the content is a placeholder?
+                                        formula_index = *index;
+                                        shared_formulas.insert(formula_index as usize, formula);
+                                    }
+                                    None => {
+                                        // We haven't met any of the daughter cells
+                                        match get_formula_index(&formula, &shared_formulas) {
+                                            // The formula is already present, use that index
+                                            Some(index) => {
+                                                formula_index = index;
+                                            }
+                                            None => {
+                                                shared_formulas.push(formula);
+                                                formula_index = shared_formulas.len() as i32 - 1;
+                                            }
+                                        };
+                                        index_map.insert(si, formula_index);
+                                    }
+                                }
+                            }
+                            None => {
+                                // It's a daughter cell
+                                match index_map.get(&si) {
+                                    Some(index) => {
+                                        formula_index = *index;
+                                    }
+                                    None => {
+                                        // Haven't bumped into the mother cell yet. We insert a placeholder.
+                                        // Note that it is perfectly possible that the formula of the mother cell
+                                        // is already in the set of array formulas. This will lead to the above mention duplicity.
+                                        // This is not a problem
+                                        let placeholder = "".to_string();
+                                        shared_formulas.push(placeholder);
+                                        formula_index = shared_formulas.len() as i32 - 1;
+                                        index_map.insert(si, formula_index);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    "array" => {
+                        return Err(XlsxError::NotImplemented("array formulas".to_string()));
+                    }
+                    "dataTable" => {
+                        return Err(XlsxError::NotImplemented("data table formulas".to_string()));
+                    }
+                    "normal" => {
+                        // Its a cell with a simple formula
+                        let formula = fs[0].text().unwrap_or("").to_string();
+                        let context = format!("{}!{}", sheet_name, cell_ref);
+                        let formula = from_a1_to_rc(formula, worksheets, context, tables.clone())?;
+
+                        match get_formula_index(&formula, &shared_formulas) {
+                            Some(index) => formula_index = index,
+                            None => {
+                                shared_formulas.push(formula);
+                                formula_index = shared_formulas.len() as i32 - 1;
+                            }
+                        }
+                    }
+                    _ => {
+                        return Err(XlsxError::Xml(format!(
+                            "Invalid formula type {:?}.",
+                            formula_type,
+                        )));
+                    }
+                }
+            }
+            let cell = get_cell_from_excel(
+                cell_value,
+                value_metadata,
+                cell_type,
+                cell_style,
+                formula_index,
+                sheet_name,
+                cell_ref,
+                shared_strings,
+            );
+            data_row.insert(column, cell);
+        }
+        sheet_data.insert(row_index, data_row);
+    }
+
+    let merge_cells = load_merge_cells(ws)?;
+
+    // Conditional Formatting
+    // <conditionalFormatting sqref="B1:B9">
+    //     <cfRule type="colorScale" priority="1">
+    //         <colorScale>
+    //             <cfvo type="min"/>
+    //             <cfvo type="max"/>
+    //             <color rgb="FFF8696B"/>
+    //             <color rgb="FFFCFCFF"/>
+    //         </colorScale>
+    //     </cfRule>
+    // </conditionalFormatting>
+    // pageSetup
+    // <pageSetup orientation="portrait" r:id="rId1"/>
+
+    Ok(Worksheet {
+        dimension,
+        cols,
+        rows,
+        shared_formulas,
+        sheet_data,
+        name: sheet_name.to_string(),
+        sheet_id,
+        state: state.to_owned(),
+        color,
+        merge_cells,
+        comments: settings.comments,
+        frozen_rows,
+        frozen_columns,
+    })
+}
+
+pub(super) fn load_sheets<R: Read + std::io::Seek>(
+    archive: &mut zip::read::ZipArchive<R>,
+    rels: &HashMap<String, Relationship>,
+    workbook: &WorkbookXML,
+    tables: &mut HashMap<String, Table>,
+    shared_strings: &mut Vec<String>,
+) -> Result<Vec<Worksheet>, XlsxError> {
+    // load comments and tables
+    let mut comments = HashMap::new();
+    for sheet in &workbook.worksheets {
+        let rel = &rels[&sheet.id];
+        if rel.rel_type.ends_with("worksheet") {
+            let path = &rel.target;
+            let path = if let Some(p) = path.strip_prefix('/') {
+                p.to_string()
+            } else {
+                format!("xl/{path}")
+            };
+            comments.insert(
+                &sheet.id,
+                load_sheet_rels(archive, &path, tables, &sheet.name)?,
+            );
+        }
+    }
+
+    // load all sheets
+    let worksheets: &Vec<String> = &workbook.worksheets.iter().map(|s| s.name.clone()).collect();
+    let mut sheets = Vec::new();
+    for sheet in &workbook.worksheets {
+        let sheet_name = &sheet.name;
+        let rel_id = &sheet.id;
+        let state = &sheet.state;
+        let rel = &rels[rel_id];
+        if rel.rel_type.ends_with("worksheet") {
+            let path = &rel.target;
+            let path = if let Some(p) = path.strip_prefix('/') {
+                p.to_string()
+            } else {
+                format!("xl/{path}")
+            };
+            let settings = SheetSettings {
+                name: sheet_name.to_string(),
+                id: sheet.sheet_id,
+                state: state.clone(),
+                comments: comments.get(rel_id).expect("").to_vec(),
+            };
+            sheets.push(load_sheet(
+                archive,
+                &path,
+                settings,
+                worksheets,
+                tables,
+                shared_strings,
+            )?);
+        }
+    }
+    Ok(sheets)
+}
+
\ No newline at end of file diff --git a/src/ironcalc/lib.rs.html b/src/ironcalc/lib.rs.html new file mode 100644 index 0000000..a9f77e9 --- /dev/null +++ b/src/ironcalc/lib.rs.html @@ -0,0 +1,123 @@ +lib.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+
//! This crate reads an xlsx file and transforms it into an internal representation ([`Model`]).
+//! An `xlsx` is a zip file containing a set of folders and `xml` files. The IronCalc json structure mimics the relevant parts of the Excel zip.
+//! Although the xlsx structure is quite complicated, it's essentials regarding the spreadsheet technology are easier to grasp.
+//!
+//! The simplest workbook folder structure might look like this:
+//!
+//! ```text
+//! docProps
+//!     app.xml
+//!     core.xml
+//!
+//! _rels
+//!     .rels
+//!
+//! xl
+//!     _rels
+//!         workbook.xml.rels
+//!     theme
+//!         theme1.xml
+//!     worksheets
+//!         sheet1.xml
+//!     calcChain.xml
+//!     styles.xml
+//!     workbook.xml
+//!     sharedStrings.xml
+//!
+//! [Content_Types].xml
+//! ```
+//!
+//! Note that more complicated workbooks will have many more files and folders.
+//! For instance charts, pivot tables, comments, tables,...
+//!
+//! The relevant json structure in IronCalc will be:
+//!
+//! ```json
+//! {
+//!     "name": "Workbook1",
+//!     "defined_names": [],
+//!     "shared_strings": [],
+//!     "worksheets": [],
+//!     "styles": {
+//!         "num_fmts": [],
+//!         "fonts": [],
+//!         "fills": [],
+//!         "borders": [],
+//!         "cell_style_xfs": [],
+//!         "cell_styles" : [],
+//!         "cell_xfs": []
+//!     }
+//! }
+//! ```
+//!
+//! Note that there is not a 1-1 correspondence but there is a close resemblance.
+//!
+//! [`Model`]: ../ironcalc/struct.Model.html
+
+pub mod compare;
+pub mod error;
+pub mod export;
+pub mod import;
+pub use ironcalc_base as base;
+
\ No newline at end of file diff --git a/src/ironcalc_base/actions.rs.html b/src/ironcalc_base/actions.rs.html new file mode 100644 index 0000000..886ca5b --- /dev/null +++ b/src/ironcalc_base/actions.rs.html @@ -0,0 +1,715 @@ +actions.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+
use crate::constants::{LAST_COLUMN, LAST_ROW};
+use crate::expressions::parser::stringify::DisplaceData;
+use crate::model::Model;
+
+// NOTE: There is a difference with Excel behaviour when deleting cells/rows/columns
+// In Excel if the whole range is deleted then it will substitute for #REF!
+// In IronCalc, if one of the edges of the range is deleted will replace the edge with #REF!
+// I feel this is unimportant for now.
+
+impl Model {
+    /// This function iterates over all cells in the model and shifts their formulas according to the displacement data.
+    ///
+    /// # Arguments
+    ///
+    /// * `displace_data` - A reference to `DisplaceData` describing the displacement's direction and magnitude.
+    fn displace_cells(&mut self, displace_data: &DisplaceData) {
+        let cells = self.get_all_cells();
+        for cell in cells {
+            self.shift_cell_formula(cell.index, cell.row, cell.column, displace_data);
+        }
+    }
+
+    /// Retrieves the column indices for a specific row in a given sheet, sorted in ascending or descending order.
+    ///
+    /// # Arguments
+    ///
+    /// * `sheet` - The sheet number to retrieve columns from.
+    /// * `row` - The row number to retrieve columns for.
+    /// * `descending` - If true, the columns are returned in descending order; otherwise, in ascending order.
+    ///
+    /// # Returns
+    ///
+    /// This function returns a `Result` containing either:
+    /// - `Ok(Vec<i32>)`: A vector of column indices for the specified row, sorted according to the `descending` flag.
+    /// - `Err(String)`: An error message if the sheet cannot be found.
+    fn get_columns_for_row(
+        &self,
+        sheet: u32,
+        row: i32,
+        descending: bool,
+    ) -> Result<Vec<i32>, String> {
+        let worksheet = self.workbook.worksheet(sheet)?;
+        if let Some(row_data) = worksheet.sheet_data.get(&row) {
+            let mut columns: Vec<i32> = row_data.keys().copied().collect();
+            columns.sort_unstable();
+            if descending {
+                columns.reverse();
+            }
+            Ok(columns)
+        } else {
+            Ok(vec![])
+        }
+    }
+
+    /// Moves the contents of cell (source_row, source_column) to (target_row, target_column).
+    ///
+    /// # Arguments
+    ///
+    /// * `sheet` - The sheet number to retrieve columns from.
+    /// * `source_row` - The row index of the cell's current location.
+    /// * `source_column` - The column index of the cell's current location.
+    /// * `target_row` - The row index of the cell's new location.
+    /// * `target_column` - The column index of the cell's new location.
+    fn move_cell(
+        &mut self,
+        sheet: u32,
+        source_row: i32,
+        source_column: i32,
+        target_row: i32,
+        target_column: i32,
+    ) -> Result<(), String> {
+        let source_cell = self
+            .workbook
+            .worksheet(sheet)?
+            .cell(source_row, source_column)
+            .ok_or("Expected Cell to exist")?;
+        let style = source_cell.get_style();
+        // FIXME: we need some user_input getter instead of get_text
+        let formula_or_value = self
+            .cell_formula(sheet, source_row, source_column)?
+            .unwrap_or_else(|| source_cell.get_text(&self.workbook.shared_strings, &self.language));
+        self.set_user_input(sheet, target_row, target_column, formula_or_value);
+        self.workbook
+            .worksheet_mut(sheet)?
+            .set_cell_style(target_row, target_column, style);
+        self.delete_cell(sheet, source_row, source_column)?;
+        Ok(())
+    }
+
+    /// Inserts one or more new columns into the model at the specified index.
+    ///
+    /// This method shifts existing columns to the right to make space for the new columns.
+    ///
+    /// # Arguments
+    ///
+    /// * `sheet` - The sheet number to retrieve columns from.
+    /// * `column` - The index at which the new columns should be inserted.
+    /// * `column_count` - The number of columns to insert.
+    pub fn insert_columns(
+        &mut self,
+        sheet: u32,
+        column: i32,
+        column_count: i32,
+    ) -> Result<(), String> {
+        if column_count <= 0 {
+            return Err("Cannot add a negative number of cells :)".to_string());
+        }
+        // check if it is possible:
+        let dimensions = self.workbook.worksheet(sheet)?.dimension();
+        let last_column = dimensions.max_column + column_count;
+        if last_column > LAST_COLUMN {
+            return Err(
+                "Cannot shift cells because that would delete cells at the end of a row"
+                    .to_string(),
+            );
+        }
+        let worksheet = self.workbook.worksheet(sheet)?;
+        let all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
+        for row in all_rows {
+            let sorted_columns = self.get_columns_for_row(sheet, row, true)?;
+            for col in sorted_columns {
+                if col >= column {
+                    self.move_cell(sheet, row, col, row, col + column_count)?;
+                } else {
+                    // Break because columns are in descending order.
+                    break;
+                }
+            }
+        }
+
+        // Update all formulas in the workbook
+        self.displace_cells(
+            &(DisplaceData::Column {
+                sheet,
+                column,
+                delta: column_count,
+            }),
+        );
+
+        Ok(())
+    }
+
+    /// Deletes one or more columns from the model starting at the specified index.
+    ///
+    /// # Arguments
+    ///
+    /// * `sheet` - The sheet number to retrieve columns from.
+    /// * `column` - The index of the first column to delete.
+    /// * `count` - The number of columns to delete.
+    pub fn delete_columns(
+        &mut self,
+        sheet: u32,
+        column: i32,
+        column_count: i32,
+    ) -> Result<(), String> {
+        if column_count <= 0 {
+            return Err("Please use insert columns instead".to_string());
+        }
+
+        // Move cells
+        let worksheet = &self.workbook.worksheet(sheet)?;
+        let mut all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
+        // We do not need to do that, but it is safer to eliminate sources of randomness in the algorithm
+        all_rows.sort_unstable();
+
+        for r in all_rows {
+            let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
+            for col in columns {
+                if col >= column {
+                    if col >= column + column_count {
+                        self.move_cell(sheet, r, col, r, col - column_count)?;
+                    } else {
+                        self.delete_cell(sheet, r, col)?;
+                    }
+                }
+            }
+        }
+        // Update all formulas in the workbook
+
+        self.displace_cells(
+            &(DisplaceData::Column {
+                sheet,
+                column,
+                delta: -column_count,
+            }),
+        );
+
+        Ok(())
+    }
+
+    /// Inserts one or more new rows into the model at the specified index.
+    ///
+    /// # Arguments
+    ///
+    /// * `sheet` - The sheet number to retrieve columns from.
+    /// * `row` - The index at which the new rows should be inserted.
+    /// * `row_count` - The number of rows to insert.
+    pub fn insert_rows(&mut self, sheet: u32, row: i32, row_count: i32) -> Result<(), String> {
+        if row_count <= 0 {
+            return Err("Cannot add a negative number of cells :)".to_string());
+        }
+        // Check if it is possible:
+        let dimensions = self.workbook.worksheet(sheet)?.dimension();
+        let last_row = dimensions.max_row + row_count;
+        if last_row > LAST_ROW {
+            return Err(
+                "Cannot shift cells because that would delete cells at the end of a column"
+                    .to_string(),
+            );
+        }
+
+        // Move cells
+        let worksheet = &self.workbook.worksheet(sheet)?;
+        let mut all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
+        all_rows.sort_unstable();
+        all_rows.reverse();
+        for r in all_rows {
+            if r >= row {
+                // We do not really need the columns in any order
+                let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
+                for column in columns {
+                    self.move_cell(sheet, r, column, r + row_count, column)?;
+                }
+            } else {
+                // Rows are in descending order
+                break;
+            }
+        }
+        // In the list of rows styles:
+        // * Add all rows above the rows we are inserting unchanged
+        // * Shift the ones below
+        let rows = &self.workbook.worksheets[sheet as usize].rows;
+        let mut new_rows = vec![];
+        for r in rows {
+            if r.r < row {
+                new_rows.push(r.clone());
+            } else if r.r >= row {
+                let mut new_row = r.clone();
+                new_row.r = r.r + row_count;
+                new_rows.push(new_row);
+            }
+        }
+        self.workbook.worksheets[sheet as usize].rows = new_rows;
+
+        // Update all formulas in the workbook
+        self.displace_cells(
+            &(DisplaceData::Row {
+                sheet,
+                row,
+                delta: row_count,
+            }),
+        );
+
+        Ok(())
+    }
+
+    /// Deletes one or more rows from the model starting at the specified index.
+    ///
+    /// # Arguments
+    ///
+    /// * `sheet` - The sheet number to retrieve columns from.
+    /// * `row` - The index of the first row to delete.
+    /// * `row_count` - The number of rows to delete.
+    pub fn delete_rows(&mut self, sheet: u32, row: i32, row_count: i32) -> Result<(), String> {
+        if row_count <= 0 {
+            return Err("Please use insert rows instead".to_string());
+        }
+        // Move cells
+        let worksheet = &self.workbook.worksheet(sheet)?;
+        let mut all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
+        all_rows.sort_unstable();
+
+        for r in all_rows {
+            if r >= row {
+                // We do not need ordered, but it is safer to eliminate sources of randomness in the algorithm
+                let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
+                if r >= row + row_count {
+                    // displace all cells in column
+                    for column in columns {
+                        self.move_cell(sheet, r, column, r - row_count, column)?;
+                    }
+                } else {
+                    // remove all cells in row
+                    // FIXME: We could just remove the entire row in one go
+                    for column in columns {
+                        self.delete_cell(sheet, r, column)?;
+                    }
+                }
+            }
+        }
+        // In the list of rows styles:
+        // * Add all rows above the rows we are deleting unchanged
+        // * Skip all those we are deleting
+        // * Shift the ones below
+        let rows = &self.workbook.worksheets[sheet as usize].rows;
+        let mut new_rows = vec![];
+        for r in rows {
+            if r.r < row {
+                new_rows.push(r.clone());
+            } else if r.r >= row + row_count {
+                let mut new_row = r.clone();
+                new_row.r = r.r - row_count;
+                new_rows.push(new_row);
+            }
+        }
+        self.workbook.worksheets[sheet as usize].rows = new_rows;
+        self.displace_cells(
+            &(DisplaceData::Row {
+                sheet,
+                row,
+                delta: -row_count,
+            }),
+        );
+        Ok(())
+    }
+
+    /// Displaces cells due to a move column action
+    /// from initial_column to target_column = initial_column + column_delta
+    /// References will be updated following:
+    /// Cell references:
+    ///    * All cell references to initial_column will go to target_column
+    ///    * All cell references to columns in between (initial_column, target_column] will be displaced one to the left
+    ///    * All other cell references are left unchanged
+    /// Ranges. This is the tricky bit:
+    ///    * Column is one of the extremes of the range. The new extreme would be target_column.
+    ///      Range is then normalized
+    ///    * Any other case, range is left unchanged.
+    /// NOTE: This does NOT move the data in the columns or move the colum styles
+    pub fn move_column_action(
+        &mut self,
+        sheet: u32,
+        column: i32,
+        delta: i32,
+    ) -> Result<(), &'static str> {
+        // Check boundaries
+        let target_column = column + delta;
+        if !(1..=LAST_COLUMN).contains(&target_column) {
+            return Err("Target column out of boundaries");
+        }
+        if !(1..=LAST_COLUMN).contains(&column) {
+            return Err("Initial column out of boundaries");
+        }
+
+        // TODO: Add the actual displacement of data and styles
+
+        // Update all formulas in the workbook
+        self.displace_cells(
+            &(DisplaceData::ColumnMove {
+                sheet,
+                column,
+                delta,
+            }),
+        );
+
+        Ok(())
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/calc_result.rs.html b/src/ironcalc_base/calc_result.rs.html new file mode 100644 index 0000000..6f07206 --- /dev/null +++ b/src/ironcalc_base/calc_result.rs.html @@ -0,0 +1,229 @@ +calc_result.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+
use std::cmp::Ordering;
+
+use crate::expressions::token::Error;
+
+#[derive(Debug, Clone, PartialEq, Eq, Copy)]
+pub struct CellReference {
+    pub sheet: u32,
+    pub column: i32,
+    pub row: i32,
+}
+
+#[derive(Debug, Clone)]
+pub struct Range {
+    pub left: CellReference,
+    pub right: CellReference,
+}
+
+#[derive(Clone)]
+pub(crate) enum CalcResult {
+    String(String),
+    Number(f64),
+    Boolean(bool),
+    Error {
+        error: Error,
+        origin: CellReference,
+        message: String,
+    },
+    Range {
+        left: CellReference,
+        right: CellReference,
+    },
+    EmptyCell,
+    EmptyArg,
+}
+
+impl CalcResult {
+    pub fn new_error(error: Error, origin: CellReference, message: String) -> CalcResult {
+        CalcResult::Error {
+            error,
+            origin,
+            message,
+        }
+    }
+    pub fn new_args_number_error(origin: CellReference) -> CalcResult {
+        CalcResult::Error {
+            error: Error::ERROR,
+            origin,
+            message: "Wrong number of arguments".to_string(),
+        }
+    }
+    pub fn is_error(&self) -> bool {
+        matches!(self, CalcResult::Error { .. })
+    }
+}
+
+impl Ord for CalcResult {
+    // ..., -2, -1, 0, 1, 2, ..., A-Z, FALSE, TRUE, empty;
+    fn cmp(&self, other: &Self) -> Ordering {
+        match (self, other) {
+            (CalcResult::Number(value1), CalcResult::Number(value2)) => {
+                if (value2 - value1).abs() < f64::EPSILON {
+                    return Ordering::Equal;
+                }
+                if value1 < value2 {
+                    return Ordering::Less;
+                }
+                Ordering::Greater
+            }
+            (CalcResult::Number(_value1), CalcResult::String(_value2)) => Ordering::Less,
+            (CalcResult::Number(_value1), CalcResult::Boolean(_value2)) => Ordering::Less,
+            (CalcResult::String(value1), CalcResult::String(value2)) => {
+                let value1 = value1.to_uppercase();
+                let value2 = value2.to_uppercase();
+                value1.cmp(&value2)
+            }
+            (CalcResult::String(_value1), CalcResult::Boolean(_value2)) => Ordering::Less,
+            (CalcResult::Boolean(value1), CalcResult::Boolean(value2)) => {
+                if value1 == value2 {
+                    return Ordering::Equal;
+                }
+                if *value1 {
+                    return Ordering::Greater;
+                }
+                Ordering::Less
+            }
+            (CalcResult::EmptyCell, CalcResult::String(_value2)) => Ordering::Greater,
+            (CalcResult::EmptyArg, CalcResult::String(_value2)) => Ordering::Greater,
+            (CalcResult::String(_value1), CalcResult::EmptyCell) => Ordering::Less,
+            (CalcResult::EmptyCell, CalcResult::Number(_value2)) => Ordering::Greater,
+            (CalcResult::EmptyArg, CalcResult::Number(_value2)) => Ordering::Greater,
+            (CalcResult::Number(_value1), CalcResult::EmptyCell) => Ordering::Less,
+            (CalcResult::EmptyCell, CalcResult::EmptyCell) => Ordering::Equal,
+            (CalcResult::EmptyCell, CalcResult::EmptyArg) => Ordering::Equal,
+            (CalcResult::EmptyArg, CalcResult::EmptyCell) => Ordering::Equal,
+            (CalcResult::EmptyArg, CalcResult::EmptyArg) => Ordering::Equal,
+            // NOTE: Errors and Ranges are not covered
+            (_, _) => Ordering::Greater,
+        }
+    }
+}
+
+impl PartialOrd for CalcResult {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl PartialEq for CalcResult {
+    fn eq(&self, other: &Self) -> bool {
+        self.cmp(other) == Ordering::Equal
+    }
+}
+
+impl Eq for CalcResult {}
+
\ No newline at end of file diff --git a/src/ironcalc_base/cast.rs.html b/src/ironcalc_base/cast.rs.html new file mode 100644 index 0000000..d8925db --- /dev/null +++ b/src/ironcalc_base/cast.rs.html @@ -0,0 +1,429 @@ +cast.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+
use crate::{
+    calc_result::{CalcResult, CellReference, Range},
+    expressions::{parser::Node, token::Error},
+    implicit_intersection::implicit_intersection,
+    model::Model,
+};
+
+impl Model {
+    pub(crate) fn get_number(
+        &mut self,
+        node: &Node,
+        cell: CellReference,
+    ) -> Result<f64, CalcResult> {
+        let result = self.evaluate_node_in_context(node, cell);
+        self.cast_to_number(result, cell)
+    }
+
+    fn cast_to_number(
+        &mut self,
+        result: CalcResult,
+        cell: CellReference,
+    ) -> Result<f64, CalcResult> {
+        match result {
+            CalcResult::Number(f) => Ok(f),
+            CalcResult::String(s) => match s.parse::<f64>() {
+                Ok(f) => Ok(f),
+                _ => Err(CalcResult::new_error(
+                    Error::VALUE,
+                    cell,
+                    "Expecting number".to_string(),
+                )),
+            },
+            CalcResult::Boolean(f) => {
+                if f {
+                    Ok(1.0)
+                } else {
+                    Ok(0.0)
+                }
+            }
+            CalcResult::EmptyCell | CalcResult::EmptyArg => Ok(0.0),
+            error @ CalcResult::Error { .. } => Err(error),
+            CalcResult::Range { left, right } => {
+                match implicit_intersection(&cell, &Range { left, right }) {
+                    Some(cell_reference) => {
+                        let result = self.evaluate_cell(cell_reference);
+                        self.cast_to_number(result, cell_reference)
+                    }
+                    None => Err(CalcResult::Error {
+                        error: Error::VALUE,
+                        origin: cell,
+                        message: "Invalid reference (number)".to_string(),
+                    }),
+                }
+            }
+        }
+    }
+
+    pub(crate) fn get_number_no_bools(
+        &mut self,
+        node: &Node,
+        cell: CellReference,
+    ) -> Result<f64, CalcResult> {
+        let result = self.evaluate_node_in_context(node, cell);
+        if matches!(result, CalcResult::Boolean(_)) {
+            return Err(CalcResult::new_error(
+                Error::VALUE,
+                cell,
+                "Expecting number".to_string(),
+            ));
+        }
+        self.cast_to_number(result, cell)
+    }
+
+    pub(crate) fn get_string(
+        &mut self,
+        node: &Node,
+        cell: CellReference,
+    ) -> Result<String, CalcResult> {
+        let result = self.evaluate_node_in_context(node, cell);
+        self.cast_to_string(result, cell)
+    }
+
+    pub(crate) fn cast_to_string(
+        &mut self,
+        result: CalcResult,
+        cell: CellReference,
+    ) -> Result<String, CalcResult> {
+        // FIXME: I think when casting a number we should convert it to_precision(x, 15)
+        // See function Exact
+        match result {
+            CalcResult::Number(f) => Ok(format!("{}", f)),
+            CalcResult::String(s) => Ok(s),
+            CalcResult::Boolean(f) => {
+                if f {
+                    Ok("TRUE".to_string())
+                } else {
+                    Ok("FALSE".to_string())
+                }
+            }
+            CalcResult::EmptyCell | CalcResult::EmptyArg => Ok("".to_string()),
+            error @ CalcResult::Error { .. } => Err(error),
+            CalcResult::Range { left, right } => {
+                match implicit_intersection(&cell, &Range { left, right }) {
+                    Some(cell_reference) => {
+                        let result = self.evaluate_cell(cell_reference);
+                        self.cast_to_string(result, cell_reference)
+                    }
+                    None => Err(CalcResult::Error {
+                        error: Error::VALUE,
+                        origin: cell,
+                        message: "Invalid reference (string)".to_string(),
+                    }),
+                }
+            }
+        }
+    }
+
+    pub(crate) fn get_boolean(
+        &mut self,
+        node: &Node,
+        cell: CellReference,
+    ) -> Result<bool, CalcResult> {
+        let result = self.evaluate_node_in_context(node, cell);
+        self.cast_to_bool(result, cell)
+    }
+
+    fn cast_to_bool(
+        &mut self,
+        result: CalcResult,
+        cell: CellReference,
+    ) -> Result<bool, CalcResult> {
+        match result {
+            CalcResult::Number(f) => {
+                if f == 0.0 {
+                    return Ok(false);
+                }
+                Ok(true)
+            }
+            CalcResult::String(s) => {
+                if s.to_lowercase() == *"true" {
+                    return Ok(true);
+                } else if s.to_lowercase() == *"false" {
+                    return Ok(false);
+                }
+                Err(CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "Expected boolean".to_string(),
+                })
+            }
+            CalcResult::Boolean(b) => Ok(b),
+            CalcResult::EmptyCell | CalcResult::EmptyArg => Ok(false),
+            error @ CalcResult::Error { .. } => Err(error),
+            CalcResult::Range { left, right } => {
+                match implicit_intersection(&cell, &Range { left, right }) {
+                    Some(cell_reference) => {
+                        let result = self.evaluate_cell(cell_reference);
+                        self.cast_to_bool(result, cell_reference)
+                    }
+                    None => Err(CalcResult::Error {
+                        error: Error::VALUE,
+                        origin: cell,
+                        message: "Invalid reference (bool)".to_string(),
+                    }),
+                }
+            }
+        }
+    }
+
+    // tries to return a reference. That is either a reference or a formula that evaluates to a range/reference
+    pub(crate) fn get_reference(
+        &mut self,
+        node: &Node,
+        cell: CellReference,
+    ) -> Result<Range, CalcResult> {
+        match node {
+            Node::ReferenceKind {
+                column,
+                absolute_column,
+                row,
+                absolute_row,
+                sheet_index,
+                sheet_name: _,
+            } => {
+                let left = CellReference {
+                    sheet: *sheet_index,
+                    row: if *absolute_row { *row } else { *row + cell.row },
+                    column: if *absolute_column {
+                        *column
+                    } else {
+                        *column + cell.column
+                    },
+                };
+
+                Ok(Range { left, right: left })
+            }
+            _ => {
+                let value = self.evaluate_node_in_context(node, cell);
+                if value.is_error() {
+                    return Err(value);
+                }
+                if let CalcResult::Range { left, right } = value {
+                    Ok(Range { left, right })
+                } else {
+                    Err(CalcResult::Error {
+                        error: Error::VALUE,
+                        origin: cell,
+                        message: "Expected reference".to_string(),
+                    })
+                }
+            }
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/cell.rs.html b/src/ironcalc_base/cell.rs.html new file mode 100644 index 0000000..8cd3205 --- /dev/null +++ b/src/ironcalc_base/cell.rs.html @@ -0,0 +1,385 @@ +cell.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+
use crate::{
+    expressions::token::Error, language::Language, number_format::to_excel_precision_str, types::*,
+};
+use serde::{Deserialize, Serialize};
+use serde_json::json;
+
+/// A CellValue is the representation of the cell content.
+#[derive(Serialize, Deserialize, Debug, PartialEq)]
+#[serde(untagged)]
+pub enum CellValue {
+    None,
+    String(String),
+    Number(f64),
+    Boolean(bool),
+}
+
+impl CellValue {
+    pub fn to_json_str(&self) -> String {
+        match &self {
+            CellValue::None => "null".to_string(),
+            CellValue::String(s) => json!(s).to_string(),
+            CellValue::Number(f) => json!(f).to_string(),
+            CellValue::Boolean(b) => json!(b).to_string(),
+        }
+    }
+}
+
+impl From<f64> for CellValue {
+    fn from(value: f64) -> Self {
+        Self::Number(value)
+    }
+}
+
+impl From<String> for CellValue {
+    fn from(value: String) -> Self {
+        Self::String(value)
+    }
+}
+
+impl From<&str> for CellValue {
+    fn from(value: &str) -> Self {
+        Self::String(value.to_string())
+    }
+}
+
+impl From<bool> for CellValue {
+    fn from(value: bool) -> Self {
+        Self::Boolean(value)
+    }
+}
+
+impl Cell {
+    /// Creates a new Cell with a shared string (`si` is the string index)
+    pub fn new_string(si: i32, s: i32) -> Cell {
+        Cell::SharedString { si, s }
+    }
+
+    /// Creates a new Cell with a number
+    pub fn new_number(v: f64, s: i32) -> Cell {
+        Cell::NumberCell { v, s }
+    }
+
+    /// Creates a new Cell with a boolean
+    pub fn new_boolean(v: bool, s: i32) -> Cell {
+        Cell::BooleanCell { v, s }
+    }
+
+    /// Creates a new Cell with an error value
+    pub fn new_error(ei: Error, s: i32) -> Cell {
+        Cell::ErrorCell { ei, s }
+    }
+
+    /// Creates a new Cell with an unevaluated formula
+    pub fn new_formula(f: i32, s: i32) -> Cell {
+        Cell::CellFormula { f, s }
+    }
+
+    /// Returns the formula of a cell if any.
+    pub fn get_formula(&self) -> Option<i32> {
+        match self {
+            Cell::CellFormula { f, .. } => Some(*f),
+            Cell::CellFormulaBoolean { f, .. } => Some(*f),
+            Cell::CellFormulaNumber { f, .. } => Some(*f),
+            Cell::CellFormulaString { f, .. } => Some(*f),
+            Cell::CellFormulaError { f, .. } => Some(*f),
+            _ => None,
+        }
+    }
+
+    pub fn has_formula(&self) -> bool {
+        self.get_formula().is_some()
+    }
+
+    pub fn set_style(&mut self, style: i32) {
+        match self {
+            Cell::EmptyCell { s, .. } => *s = style,
+            Cell::BooleanCell { s, .. } => *s = style,
+            Cell::NumberCell { s, .. } => *s = style,
+            Cell::ErrorCell { s, .. } => *s = style,
+            Cell::SharedString { s, .. } => *s = style,
+            Cell::CellFormula { s, .. } => *s = style,
+            Cell::CellFormulaBoolean { s, .. } => *s = style,
+            Cell::CellFormulaNumber { s, .. } => *s = style,
+            Cell::CellFormulaString { s, .. } => *s = style,
+            Cell::CellFormulaError { s, .. } => *s = style,
+        };
+    }
+
+    pub fn get_style(&self) -> i32 {
+        match self {
+            Cell::EmptyCell { s, .. } => *s,
+            Cell::BooleanCell { s, .. } => *s,
+            Cell::NumberCell { s, .. } => *s,
+            Cell::ErrorCell { s, .. } => *s,
+            Cell::SharedString { s, .. } => *s,
+            Cell::CellFormula { s, .. } => *s,
+            Cell::CellFormulaBoolean { s, .. } => *s,
+            Cell::CellFormulaNumber { s, .. } => *s,
+            Cell::CellFormulaString { s, .. } => *s,
+            Cell::CellFormulaError { s, .. } => *s,
+        }
+    }
+
+    pub fn get_type(&self) -> CellType {
+        match self {
+            Cell::EmptyCell { .. } => CellType::Number,
+            Cell::BooleanCell { .. } => CellType::LogicalValue,
+            Cell::NumberCell { .. } => CellType::Number,
+            Cell::ErrorCell { .. } => CellType::ErrorValue,
+            Cell::SharedString { .. } => CellType::Text,
+            Cell::CellFormula { .. } => CellType::Number,
+            Cell::CellFormulaBoolean { .. } => CellType::LogicalValue,
+            Cell::CellFormulaNumber { .. } => CellType::Number,
+            Cell::CellFormulaString { .. } => CellType::Text,
+            Cell::CellFormulaError { .. } => CellType::ErrorValue,
+        }
+    }
+
+    pub fn get_text(&self, shared_strings: &[String], language: &Language) -> String {
+        match self.value(shared_strings, language) {
+            CellValue::None => "".to_string(),
+            CellValue::String(v) => v,
+            CellValue::Boolean(v) => v.to_string().to_uppercase(),
+            CellValue::Number(v) => to_excel_precision_str(v),
+        }
+    }
+
+    pub fn value(&self, shared_strings: &[String], language: &Language) -> CellValue {
+        match self {
+            Cell::EmptyCell { .. } => CellValue::None,
+            Cell::BooleanCell { v, s: _ } => CellValue::Boolean(*v),
+            Cell::NumberCell { v, s: _ } => CellValue::Number(*v),
+            Cell::ErrorCell { ei, .. } => {
+                let v = ei.to_localized_error_string(language);
+                CellValue::String(v)
+            }
+            Cell::SharedString { si, .. } => {
+                let s = shared_strings.get(*si as usize);
+                let v = match s {
+                    Some(str) => str.clone(),
+                    None => "".to_string(),
+                };
+                CellValue::String(v)
+            }
+            Cell::CellFormula { .. } => CellValue::String("#ERROR!".to_string()),
+            Cell::CellFormulaBoolean { v, .. } => CellValue::Boolean(*v),
+            Cell::CellFormulaNumber { v, .. } => CellValue::Number(*v),
+            Cell::CellFormulaString { v, .. } => CellValue::String(v.clone()),
+            Cell::CellFormulaError { ei, .. } => {
+                let v = ei.to_localized_error_string(language);
+                CellValue::String(v)
+            }
+        }
+    }
+
+    pub fn formatted_value<F>(
+        &self,
+        shared_strings: &[String],
+        language: &Language,
+        format_number: F,
+    ) -> String
+    where
+        F: Fn(f64) -> String,
+    {
+        match self.value(shared_strings, language) {
+            CellValue::None => "".to_string(),
+            CellValue::String(value) => value,
+            CellValue::Boolean(value) => value.to_string().to_uppercase(),
+            CellValue::Number(value) => format_number(value),
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/constants.rs.html b/src/ironcalc_base/constants.rs.html new file mode 100644 index 0000000..0f1cdd2 --- /dev/null +++ b/src/ironcalc_base/constants.rs.html @@ -0,0 +1,33 @@ +constants.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
/// Excel compatibility values
+/// COLUMN_WIDTH and ROW_HEIGHT are pixel values
+/// A column width of Excel value `w` will result in `w * COLUMN_WIDTH_FACTOR` pixels
+/// Note that these constants are inlined
+pub(crate) const DEFAULT_COLUMN_WIDTH: f64 = 100.0;
+pub(crate) const DEFAULT_ROW_HEIGHT: f64 = 21.0;
+pub(crate) const COLUMN_WIDTH_FACTOR: f64 = 12.0;
+pub(crate) const ROW_HEIGHT_FACTOR: f64 = 2.0;
+
+pub(crate) const LAST_COLUMN: i32 = 16_384;
+pub(crate) const LAST_ROW: i32 = 1_048_576;
+
+// 693_594 is computed as:
+// NaiveDate::from_ymd(1900, 1, 1).num_days_from_ce() - 2
+// The 2 days offset is because of Excel 1900 bug
+pub(crate) const EXCEL_DATE_BASE: i32 = 693_594;
+
\ No newline at end of file diff --git a/src/ironcalc_base/diffs.rs.html b/src/ironcalc_base/diffs.rs.html new file mode 100644 index 0000000..9acb429 --- /dev/null +++ b/src/ironcalc_base/diffs.rs.html @@ -0,0 +1,273 @@ +diffs.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+
use crate::{
+    expressions::{
+        parser::{
+            move_formula::ref_is_in_area,
+            stringify::{to_string, to_string_displaced, DisplaceData},
+            walk::forward_references,
+        },
+        types::{Area, CellReferenceIndex, CellReferenceRC},
+    },
+    model::Model,
+};
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
+#[serde(untagged, deny_unknown_fields)]
+pub enum CellValue {
+    Value(String),
+    None,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
+pub struct SetCellValue {
+    cell: CellReferenceIndex,
+    new_value: CellValue,
+    old_value: CellValue,
+}
+
+impl Model {
+    pub(crate) fn shift_cell_formula(
+        &mut self,
+        sheet: u32,
+        row: i32,
+        column: i32,
+        displace_data: &DisplaceData,
+    ) {
+        if let Some(f) = self
+            .workbook
+            .worksheet(sheet)
+            .expect("Worksheet must exist")
+            .cell(row, column)
+            .expect("Cell must exist")
+            .get_formula()
+        {
+            let node = &self.parsed_formulas[sheet as usize][f as usize].clone();
+            let cell_reference = CellReferenceRC {
+                sheet: self.workbook.worksheets[sheet as usize].get_name(),
+                row,
+                column,
+            };
+            // FIXME: This is not a very performant way if the formula has changed :S.
+            let formula = to_string(node, &cell_reference);
+            let formula_displaced = to_string_displaced(node, &cell_reference, displace_data);
+            if formula != formula_displaced {
+                self.update_cell_with_formula(sheet, row, column, format!("={formula_displaced}"))
+                    .expect("Failed to shift cell formula");
+            }
+        }
+    }
+
+    pub fn forward_references(
+        &mut self,
+        source_area: &Area,
+        target: &CellReferenceIndex,
+    ) -> Result<Vec<SetCellValue>, String> {
+        let mut diff_list: Vec<SetCellValue> = Vec::new();
+        let target_area = &Area {
+            sheet: target.sheet,
+            row: target.row,
+            column: target.column,
+            width: source_area.width,
+            height: source_area.height,
+        };
+        // Walk over every formula
+        let cells = self.get_all_cells();
+        for cell in cells {
+            if let Some(f) = self
+                .workbook
+                .worksheet(cell.index)
+                .expect("Worksheet must exist")
+                .cell(cell.row, cell.column)
+                .expect("Cell must exist")
+                .get_formula()
+            {
+                let sheet = cell.index;
+                let row = cell.row;
+                let column = cell.column;
+
+                // If cell is in the source or target area, skip
+                if ref_is_in_area(sheet, row, column, source_area)
+                    || ref_is_in_area(sheet, row, column, target_area)
+                {
+                    continue;
+                }
+
+                // Get the formula
+                // Get a copy of the AST
+                let node = &mut self.parsed_formulas[sheet as usize][f as usize].clone();
+                let cell_reference = CellReferenceRC {
+                    sheet: self.workbook.worksheets[sheet as usize].get_name(),
+                    column: cell.column,
+                    row: cell.row,
+                };
+                let context = CellReferenceIndex { sheet, column, row };
+                let formula = to_string(node, &cell_reference);
+                let target_sheet_name = &self.workbook.worksheets[target.sheet as usize].name;
+                forward_references(
+                    node,
+                    &context,
+                    source_area,
+                    target.sheet,
+                    target_sheet_name,
+                    target.row,
+                    target.column,
+                );
+
+                // If the string representation of the formula has changed update the cell
+                let updated_formula = to_string(node, &cell_reference);
+                if formula != updated_formula {
+                    self.update_cell_with_formula(
+                        sheet,
+                        row,
+                        column,
+                        format!("={updated_formula}"),
+                    )?;
+                    // Update the diff list
+                    diff_list.push(SetCellValue {
+                        cell: CellReferenceIndex { sheet, column, row },
+                        new_value: CellValue::Value(format!("={}", updated_formula)),
+                        old_value: CellValue::Value(format!("={}", formula)),
+                    });
+                }
+            }
+        }
+        Ok(diff_list)
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/expressions/lexer/mod.rs.html b/src/ironcalc_base/expressions/lexer/mod.rs.html new file mode 100644 index 0000000..3d4e227 --- /dev/null +++ b/src/ironcalc_base/expressions/lexer/mod.rs.html @@ -0,0 +1,1525 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+
//! A tokenizer for spreadsheet formulas.
+//!
+//! This is meant to feed a formula parser.
+//!
+//! You will need to instantiate it with a language and a locale.
+//!
+//! It supports two working modes:
+//!
+//! 1. A1 or display mode
+//!    This is for user formulas. References are like `D4`, `D$4` or `F5:T10`
+//! 2. R1C1, internal or runtime mode
+//!    A reference like R1C1 refers to $A$1 and R3C4 to $D$4
+//!    R[2]C[5] refers to a cell two rows below and five columns to the right
+//!    It uses the 'en' locale and language.
+//!    This is used internally at runtime.
+//!
+//! Formulas look different in different locales:
+//!
+//! =IF(A1, B1, NA()) versus =IF(A1; B1; NA())
+//!
+//! Also numbers are different:
+//!
+//! 1,123.45 versus 1.123,45
+//!
+//! The names of the errors and functions are different in different languages,
+//! but they stay the same in different locales.
+//!
+//! Note that in IronCalc if you are using a locale different from 'en' or a language different from 'en'
+//! you will still need the 'en' locale and language because formulas are stored in that language and locale
+//!
+//! # Examples:
+//! ```
+//! use ironcalc_base::expressions::lexer::{Lexer, LexerMode};
+//! use ironcalc_base::expressions::token::{TokenType, OpCompare};
+//! use ironcalc_base::locale::get_locale;
+//! use ironcalc_base::language::get_language;
+//!
+//! let locale = get_locale("en").unwrap();
+//! let language = get_language("en").unwrap();
+//! let mut lexer = Lexer::new("=A1*SUM(Sheet2!C3:D5)", LexerMode::A1, &locale, &language);
+//! assert_eq!(lexer.next_token(), TokenType::Compare(OpCompare::Equal));
+//! assert!(matches!(lexer.next_token(), TokenType::Reference { .. }));
+//! ```
+
+use crate::expressions::token::{OpCompare, OpProduct, OpSum};
+
+use crate::language::Language;
+use crate::locale::Locale;
+
+use super::token::{index, Error, TokenType};
+use super::types::*;
+use super::utils;
+
+pub mod util;
+
+#[cfg(test)]
+mod test;
+
+mod ranges;
+mod structured_references;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct LexerError {
+    pub position: usize,
+    pub message: String,
+}
+
+pub(super) type Result<T> = std::result::Result<T, LexerError>;
+
+#[derive(Clone, PartialEq, Eq)]
+pub enum LexerMode {
+    A1,
+    R1C1,
+}
+
+/// Tokenize an input
+#[derive(Clone)]
+pub struct Lexer {
+    position: usize,
+    next_token_position: Option<usize>,
+    len: usize,
+    chars: Vec<char>,
+    mode: LexerMode,
+    locale: Locale,
+    language: Language,
+}
+
+impl Lexer {
+    /// Creates a new `Lexer` that returns the tokens of a formula.
+    pub fn new(formula: &str, mode: LexerMode, locale: &Locale, language: &Language) -> Lexer {
+        let chars: Vec<char> = formula.chars().collect();
+        let len = chars.len();
+        Lexer {
+            chars,
+            position: 0,
+            next_token_position: None,
+            len,
+            mode,
+            locale: locale.clone(),
+            language: language.clone(),
+        }
+    }
+
+    /// Changes the lexer mode
+    pub fn set_lexer_mode(&mut self, mode: LexerMode) {
+        self.mode = mode;
+    }
+
+    // FIXME: I don't think we should have `is_a1_mode` and   `get_formula`.
+    // The caller already knows those two
+
+    /// Returns true if mode is A1
+    pub fn is_a1_mode(&self) -> bool {
+        self.mode == LexerMode::A1
+    }
+
+    /// Returns the formula
+    pub fn get_formula(&self) -> String {
+        self.chars.iter().collect()
+    }
+
+    // FIXME: This is used to get the "marked tokens"
+    // I think a better API would be to return the marked tokens
+    /// Returns the position of the lexer
+    pub fn get_position(&self) -> i32 {
+        self.position as i32
+    }
+
+    /// Resets the formula
+    pub fn set_formula(&mut self, content: &str) {
+        self.chars = content.chars().collect();
+        self.len = self.chars.len();
+        self.position = 0;
+        self.next_token_position = None;
+    }
+
+    /// Returns an error if the token is not the expected one.
+    pub fn expect(&mut self, tk: TokenType) -> Result<()> {
+        let nt = self.next_token();
+        if index(&nt) != index(&tk) {
+            return Err(self.set_error(&format!("Error, expected {}", tk), self.position));
+        }
+        Ok(())
+    }
+
+    /// Checks the next token without advancing position
+    /// See also [advance_token](Self::advance_token)
+    pub fn peek_token(&mut self) -> TokenType {
+        let position = self.position;
+        let tk = self.next_token();
+        self.next_token_position = Some(self.position);
+        self.position = position;
+        tk
+    }
+
+    /// Advances position. This is used in conjunction with [`peek_token`](Self::peek_token)
+    /// It is a noop if the has not been a previous peek_token
+    pub fn advance_token(&mut self) {
+        if let Some(position) = self.next_token_position {
+            self.position = position;
+            self.next_token_position = None;
+        }
+    }
+
+    /// Returns the next token
+    pub fn next_token(&mut self) -> TokenType {
+        self.next_token_position = None;
+        self.consume_whitespace();
+
+        match self.read_next_char() {
+            Some(char) => {
+                match char {
+                    '+' => TokenType::Addition(OpSum::Add),
+                    '-' => TokenType::Addition(OpSum::Minus),
+                    '*' => TokenType::Product(OpProduct::Times),
+                    '/' => TokenType::Product(OpProduct::Divide),
+                    '(' => TokenType::LeftParenthesis,
+                    ')' => TokenType::RightParenthesis,
+                    '=' => TokenType::Compare(OpCompare::Equal),
+                    '{' => TokenType::LeftBrace,
+                    '}' => TokenType::RightBrace,
+                    '[' => TokenType::LeftBracket,
+                    ']' => TokenType::RightBracket,
+                    ':' => TokenType::Colon,
+                    ';' => TokenType::Semicolon,
+                    ',' => {
+                        if self.locale.numbers.symbols.decimal == "," {
+                            match self.consume_number(',') {
+                                Ok(number) => TokenType::Number(number),
+                                Err(error) => TokenType::Illegal(error),
+                            }
+                        } else {
+                            TokenType::Comma
+                        }
+                    }
+                    '.' => {
+                        if self.locale.numbers.symbols.decimal == "." {
+                            match self.consume_number('.') {
+                                Ok(number) => TokenType::Number(number),
+                                Err(error) => TokenType::Illegal(error),
+                            }
+                        } else {
+                            // There is no TokenType::PERIOD
+                            TokenType::Illegal(self.set_error("Expecting a number", self.position))
+                        }
+                    }
+                    '!' => TokenType::Bang,
+                    '^' => TokenType::Power,
+                    '%' => TokenType::Percent,
+                    '&' => TokenType::And,
+                    '$' => self.consume_absolute_reference(),
+                    '<' => {
+                        let next_token = self.peek_char();
+                        if next_token == Some('=') {
+                            self.position += 1;
+                            TokenType::Compare(OpCompare::LessOrEqualThan)
+                        } else if next_token == Some('>') {
+                            self.position += 1;
+                            TokenType::Compare(OpCompare::NonEqual)
+                        } else {
+                            TokenType::Compare(OpCompare::LessThan)
+                        }
+                    }
+                    '>' => {
+                        if self.peek_char() == Some('=') {
+                            self.position += 1;
+                            TokenType::Compare(OpCompare::GreaterOrEqualThan)
+                        } else {
+                            TokenType::Compare(OpCompare::GreaterThan)
+                        }
+                    }
+                    '#' => self.consume_error(),
+                    '"' => TokenType::String(self.consume_string()),
+                    '\'' => self.consume_quoted_sheet_reference(),
+                    '0'..='9' => {
+                        let position = self.position - 1;
+                        match self.consume_number(char) {
+                            Ok(number) => {
+                                if self.peek_token() == TokenType::Colon
+                                    && self.mode == LexerMode::A1
+                                {
+                                    // Its a row range  3:5
+                                    // FIXME: There are faster ways of parsing this
+                                    // Like checking that 'number' is integer and that the next token is integer
+                                    self.position = position;
+                                    match self.consume_range_a1() {
+                                        Ok(ParsedRange { left, right }) => {
+                                            if let Some(right) = right {
+                                                TokenType::Range {
+                                                    sheet: None,
+                                                    left,
+                                                    right,
+                                                }
+                                            } else {
+                                                TokenType::Illegal(
+                                                    self.set_error("Expecting row range", position),
+                                                )
+                                            }
+                                        }
+                                        Err(error) => {
+                                            // Examples:
+                                            //   * 'Sheet 1'!3.4:5
+                                            //   * 'Sheet 1'!3:A2
+                                            //   * 'Sheet 1'!3:
+                                            TokenType::Illegal(error)
+                                        }
+                                    }
+                                } else {
+                                    TokenType::Number(number)
+                                }
+                            }
+                            Err(error) => {
+                                // tried to read a number but failed
+                                self.position = self.len;
+                                TokenType::Illegal(error)
+                            }
+                        }
+                    }
+                    _ => {
+                        if char.is_alphabetic() || char == '_' {
+                            // At this point is one of the following:
+                            //   1. A range with sheet: Sheet3!A3:D7
+                            //   2. A boolean: TRUE or FALSE (dependent on the language)
+                            //   3. A reference like WS34 or R3C5
+                            //   4. A range without sheet ER4:ER7
+                            //   5. A column range E:E
+                            //   6. An identifier like a function name or a defined name
+                            //   7. A range operator A1:OFFSET(...)
+                            //   8. An Invalid token
+                            let position = self.position;
+                            self.position -= 1;
+                            let name = self.consume_identifier();
+                            let position_indent = self.position;
+
+                            let peek_char = self.peek_char();
+                            let next_char_is_colon = self.peek_char() == Some(':');
+
+                            if peek_char == Some('!') {
+                                // reference
+                                self.position += 1;
+                                return self.consume_range(Some(name));
+                            } else if peek_char == Some('$') {
+                                self.position = position - 1;
+                                return self.consume_range(None);
+                            }
+                            let name_upper = name.to_ascii_uppercase();
+                            if name_upper == self.language.booleans.true_value {
+                                return TokenType::Boolean(true);
+                            } else if name_upper == self.language.booleans.false_value {
+                                return TokenType::Boolean(false);
+                            }
+                            if self.mode == LexerMode::A1 {
+                                let parsed_reference = utils::parse_reference_a1(&name_upper);
+                                if parsed_reference.is_some()
+                                    || (utils::is_valid_column(name_upper.trim_start_matches('$'))
+                                        && next_char_is_colon)
+                                {
+                                    self.position = position - 1;
+                                    match self.consume_range_a1() {
+                                        Ok(ParsedRange { left, right }) => {
+                                            if let Some(right) = right {
+                                                return TokenType::Range {
+                                                    sheet: None,
+                                                    left,
+                                                    right,
+                                                };
+                                            } else {
+                                                return TokenType::Reference {
+                                                    sheet: None,
+                                                    column: left.column,
+                                                    row: left.row,
+                                                    absolute_row: left.absolute_row,
+                                                    absolute_column: left.absolute_column,
+                                                };
+                                            }
+                                        }
+                                        Err(error) => {
+                                            // This could be the range operator: ":"
+                                            if let Some(r) = parsed_reference {
+                                                if next_char_is_colon {
+                                                    self.position = position_indent;
+                                                    return TokenType::Reference {
+                                                        sheet: None,
+                                                        row: r.row,
+                                                        column: r.column,
+                                                        absolute_column: r.absolute_column,
+                                                        absolute_row: r.absolute_row,
+                                                    };
+                                                }
+                                            }
+                                            self.position = self.len;
+                                            return TokenType::Illegal(error);
+                                        }
+                                    }
+                                } else if utils::is_valid_identifier(&name) {
+                                    if peek_char == Some('[') {
+                                        if let Ok(r) = self.consume_structured_reference(&name) {
+                                            return r;
+                                        }
+                                        return TokenType::Illegal(self.set_error(
+                                            "Invalid structured reference",
+                                            self.position,
+                                        ));
+                                    }
+                                    return TokenType::Ident(name);
+                                } else {
+                                    return TokenType::Illegal(
+                                        self.set_error("Invalid identifier (A1)", self.position),
+                                    );
+                                }
+                            } else {
+                                let pos = self.position;
+                                self.position = position - 1;
+                                match self.consume_range_r1c1() {
+                                    // it's a valid R1C1 range
+                                    // We need to check it's not something like R1C1P
+                                    Ok(ParsedRange { left, right }) => {
+                                        if pos > self.position {
+                                            self.position = pos;
+                                            if utils::is_valid_identifier(&name) {
+                                                return TokenType::Ident(name);
+                                            } else {
+                                                self.position = self.len;
+                                                return TokenType::Illegal(
+                                                    self.set_error(
+                                                        "Invalid identifier (R1C1)",
+                                                        pos,
+                                                    ),
+                                                );
+                                            }
+                                        }
+                                        if let Some(right) = right {
+                                            return TokenType::Range {
+                                                sheet: None,
+                                                left,
+                                                right,
+                                            };
+                                        } else {
+                                            return TokenType::Reference {
+                                                sheet: None,
+                                                column: left.column,
+                                                row: left.row,
+                                                absolute_row: left.absolute_row,
+                                                absolute_column: left.absolute_column,
+                                            };
+                                        }
+                                    }
+                                    Err(error) => {
+                                        self.position = position - 1;
+                                        if let Ok(r) = self.consume_reference_r1c1() {
+                                            if self.peek_char() == Some(':') {
+                                                return TokenType::Reference {
+                                                    sheet: None,
+                                                    row: r.row,
+                                                    column: r.column,
+                                                    absolute_column: r.absolute_column,
+                                                    absolute_row: r.absolute_row,
+                                                };
+                                            }
+                                        }
+                                        self.position = pos;
+
+                                        if utils::is_valid_identifier(&name) {
+                                            return TokenType::Ident(name);
+                                        } else {
+                                            return TokenType::Illegal(self.set_error(
+                                                &format!("Invalid identifier (R1C1): {name}"),
+                                                error.position,
+                                            ));
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        TokenType::Illegal(self.set_error("Unknown error", self.position))
+                    }
+                }
+            }
+            None => TokenType::EOF,
+        }
+    }
+
+    // Private methods
+
+    fn set_error(&mut self, message: &str, position: usize) -> LexerError {
+        self.position = self.len;
+        LexerError {
+            position,
+            message: message.to_string(),
+        }
+    }
+
+    fn peek_char(&mut self) -> Option<char> {
+        let position = self.position;
+        if position < self.len {
+            Some(self.chars[position])
+        } else {
+            None
+        }
+    }
+
+    fn expect_char(&mut self, ch_expected: char) -> Result<()> {
+        let position = self.position;
+        if position >= self.len {
+            return Err(self.set_error(
+                &format!("Error, expected {} found EOF", &ch_expected),
+                self.position,
+            ));
+        } else {
+            let ch = self.chars[position];
+            if ch_expected != ch {
+                return Err(self.set_error(
+                    &format!("Error, expected {} found {}", &ch_expected, &ch),
+                    self.position,
+                ));
+            }
+            self.position += 1;
+        }
+        Ok(())
+    }
+
+    fn read_next_char(&mut self) -> Option<char> {
+        let position = self.position;
+        if position < self.len {
+            self.position = position + 1;
+            Some(self.chars[position])
+        } else {
+            None
+        }
+    }
+
+    // Consumes an integer from the input stream
+    fn consume_integer(&mut self, first: char) -> Result<i32> {
+        let mut position = self.position;
+        let len = self.len;
+        let mut chars = first.to_string();
+        while position < len {
+            let next_char = self.chars[position];
+            if next_char.is_ascii_digit() {
+                chars.push(next_char);
+            } else {
+                break;
+            }
+            position += 1;
+        }
+        self.position = position;
+        chars.parse::<i32>().map_err(|_| LexerError {
+            position,
+            message: format!("Failed to parse to int: {}", chars),
+        })
+    }
+
+    // Consumes a number in the current locale.
+    // It only takes into account the decimal separator
+    // Note that we do not parse the thousands separator
+    // Let's say ',' is the thousands separator. Then 1,234 would be an error.
+    // This is ok for most cases:
+    // =IF(A1=1,234, TRUE, FALSE) will not work
+    // If a user introduces a single number in the cell 1,234 we should be able to parse
+    // and format the cell appropriately
+    fn consume_number(&mut self, first: char) -> Result<f64> {
+        let mut position = self.position;
+        let len = self.len;
+        let mut chars = first.to_string();
+        // numbers before the decimal point
+        while position < len {
+            let x = self.chars[position];
+            if x.is_ascii_digit() {
+                chars.push(x);
+            } else {
+                break;
+            }
+            position += 1;
+        }
+        if position < len && self.chars[position].to_string() == self.locale.numbers.symbols.decimal
+        {
+            // numbers after the decimal point
+            chars.push('.');
+            position += 1;
+            while position < len {
+                let x = self.chars[position];
+                if x.is_ascii_digit() {
+                    chars.push(x);
+                } else {
+                    break;
+                }
+                position += 1;
+            }
+        }
+        if position + 1 < len && (self.chars[position] == 'e' || self.chars[position] == 'E') {
+            // exponential side
+            let x = self.chars[position + 1];
+            if x == '-' || x == '+' || x.is_ascii_digit() {
+                chars.push('e');
+                chars.push(x);
+                position += 2;
+                while position < len {
+                    let x = self.chars[position];
+                    if x.is_ascii_digit() {
+                        chars.push(x);
+                    } else {
+                        break;
+                    }
+                    position += 1;
+                }
+            }
+        }
+        self.position = position;
+        match chars.parse::<f64>() {
+            Err(_) => {
+                Err(self.set_error(&format!("Failed to parse to double: {}", chars), position))
+            }
+            Ok(v) => Ok(v),
+        }
+    }
+
+    // Consumes an identifier from the input stream
+    fn consume_identifier(&mut self) -> String {
+        let mut position = self.position;
+        while position < self.len {
+            let next_char = self.chars[position];
+            if next_char.is_alphanumeric() || next_char == '_' || next_char == '.' {
+                position += 1;
+            } else {
+                break;
+            }
+        }
+        let chars = self.chars[self.position..position].iter().collect();
+        self.position = position;
+        chars
+    }
+
+    fn consume_string(&mut self) -> String {
+        let mut position = self.position;
+        let len = self.len;
+        let mut chars = "".to_string();
+        while position < len {
+            let x = self.chars[position];
+            position += 1;
+            if x != '"' {
+                chars.push(x);
+            } else if position < len && self.chars[position] == '"' {
+                chars.push(x);
+                chars.push(self.chars[position]);
+                position += 1;
+            } else {
+                break;
+            }
+        }
+        self.position = position;
+        chars
+    }
+
+    // Consumes a quoted string from input
+    // 'This is a quoted string'
+    // ' Also is a ''quoted'' string'
+    // Returns an error if it does not find a closing quote
+    fn consume_single_quote_string(&mut self) -> Result<String> {
+        let mut position = self.position;
+        let len = self.len;
+        let mut success = false;
+        let mut needs_escape = false;
+        while position < len {
+            let next_char = self.chars[position];
+            position += 1;
+            if next_char == '\'' {
+                if position == len {
+                    success = true;
+                    break;
+                }
+                if self.chars[position] != '\'' {
+                    success = true;
+                    break;
+                } else {
+                    // In Excel we escape "'" with "''"
+                    needs_escape = true;
+                    position += 1;
+                }
+            }
+        }
+        if !success {
+            // We reached the end without the closing quote
+            return Err(self.set_error("Expected closing \"'\" but found end of input", position));
+        }
+        let chars: String = self.chars[self.position..position - 1].iter().collect();
+        self.position = position;
+        if needs_escape {
+            // In most cases we will not needs escaping so this would be an overkill
+            return Ok(chars.replace("''", "'"));
+        }
+
+        Ok(chars)
+    }
+
+    // Reads an error from the input stream
+    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;
+            return TokenType::Error(Error::REF);
+        } else if rest_of_formula.starts_with(&errors.name) {
+            self.position += errors.name.chars().count() - 1;
+            return TokenType::Error(Error::NAME);
+        } else if rest_of_formula.starts_with(&errors.value) {
+            self.position += errors.value.chars().count() - 1;
+            return TokenType::Error(Error::VALUE);
+        } else if rest_of_formula.starts_with(&errors.div) {
+            self.position += errors.div.chars().count() - 1;
+            return TokenType::Error(Error::DIV);
+        } else if rest_of_formula.starts_with(&errors.na) {
+            self.position += errors.na.chars().count() - 1;
+            return TokenType::Error(Error::NA);
+        } else if rest_of_formula.starts_with(&errors.num) {
+            self.position += errors.num.chars().count() - 1;
+            return TokenType::Error(Error::NUM);
+        } else if rest_of_formula.starts_with(&errors.error) {
+            self.position += errors.error.chars().count() - 1;
+            return TokenType::Error(Error::ERROR);
+        } else if rest_of_formula.starts_with(&errors.nimpl) {
+            self.position += errors.nimpl.chars().count() - 1;
+            return TokenType::Error(Error::NIMPL);
+        } else if rest_of_formula.starts_with(&errors.spill) {
+            self.position += errors.spill.chars().count() - 1;
+            return TokenType::Error(Error::SPILL);
+        } else if rest_of_formula.starts_with(&errors.calc) {
+            self.position += errors.calc.chars().count() - 1;
+            return TokenType::Error(Error::CALC);
+        } else if rest_of_formula.starts_with(&errors.null) {
+            self.position += errors.null.chars().count() - 1;
+            return TokenType::Error(Error::NULL);
+        } else if rest_of_formula.starts_with(&errors.circ) {
+            self.position += errors.circ.chars().count() - 1;
+            return TokenType::Error(Error::CIRC);
+        }
+        TokenType::Illegal(self.set_error("Invalid error.", self.position))
+    }
+
+    fn consume_whitespace(&mut self) {
+        let mut position = self.position;
+        let len = self.len;
+        while position < len {
+            let x = self.chars[position];
+            if !x.is_whitespace() {
+                break;
+            }
+            position += 1;
+        }
+        self.position = position;
+    }
+
+    fn consume_absolute_reference(&mut self) -> TokenType {
+        // This is an absolute reference.
+        // $A$4
+        if self.mode == LexerMode::R1C1 {
+            return TokenType::Illegal(
+                self.set_error("Cannot parse A1 reference in R1C1 mode", self.position),
+            );
+        }
+        self.position -= 1;
+        self.consume_range(None)
+    }
+
+    fn consume_quoted_sheet_reference(&mut self) -> TokenType {
+        // This is a reference:
+        // 'First Sheet'!A34
+        let sheet_name = match self.consume_single_quote_string() {
+            Ok(v) => v,
+            Err(error) => {
+                return TokenType::Illegal(error);
+            }
+        };
+        if self.next_token() != TokenType::Bang {
+            return TokenType::Illegal(self.set_error("Expected '!'", self.position));
+        }
+        self.consume_range(Some(sheet_name))
+    }
+
+    fn consume_range(&mut self, sheet: Option<String>) -> TokenType {
+        let m = if self.mode == LexerMode::A1 {
+            self.consume_range_a1()
+        } else {
+            self.consume_range_r1c1()
+        };
+        match m {
+            Ok(ParsedRange { left, right }) => {
+                if let Some(right) = right {
+                    TokenType::Range { sheet, left, right }
+                } else {
+                    TokenType::Reference {
+                        sheet,
+                        column: left.column,
+                        row: left.row,
+                        absolute_row: left.absolute_row,
+                        absolute_column: left.absolute_column,
+                    }
+                }
+            }
+            Err(error) => TokenType::Illegal(error),
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/expressions/lexer/ranges.rs.html b/src/ironcalc_base/expressions/lexer/ranges.rs.html new file mode 100644 index 0000000..f51bea0 --- /dev/null +++ b/src/ironcalc_base/expressions/lexer/ranges.rs.html @@ -0,0 +1,639 @@ +ranges.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+
use crate::constants::{LAST_COLUMN, LAST_ROW};
+use crate::expressions::{token::TokenType, utils::column_to_number};
+
+use super::Lexer;
+use super::{ParsedRange, ParsedReference, Result};
+
+impl Lexer {
+    // Consumes a reference in A1 style like:
+    // AS23, $AS23, AS$23, $AS$23, R12
+    // Or returns an error
+    fn consume_reference_a1(&mut self) -> Result<ParsedReference> {
+        let mut absolute_column = false;
+        let mut absolute_row = false;
+        let mut position = self.position;
+        let len = self.len;
+        if position < len && self.chars[position] == '$' {
+            absolute_column = true;
+            position += 1;
+        }
+        let mut column = "".to_string();
+        while position < len {
+            let x = self.chars[position].to_ascii_uppercase();
+            match x {
+                'A'..='Z' => column.push(x),
+                _ => break,
+            }
+            position += 1;
+        }
+        if column.is_empty() {
+            return Err(self.set_error("Failed to parse reference", position));
+        }
+        if position < len && self.chars[position] == '$' {
+            absolute_row = true;
+            position += 1;
+        }
+        let mut row = "".to_string();
+        while position < len {
+            let x = self.chars[position];
+            match x {
+                '0'..='9' => row.push(x),
+                _ => break,
+            }
+            position += 1;
+        }
+        // Note that row numbers could start with 0
+        self.position = position;
+        let column = column_to_number(&column).map_err(|error| self.set_error(&error, position))?;
+
+        match row.parse::<i32>() {
+            Ok(row) => {
+                if row > LAST_ROW {
+                    return Err(self.set_error("Row too large in reference", position));
+                }
+                Ok(ParsedReference {
+                    column,
+                    row,
+                    absolute_column,
+                    absolute_row,
+                })
+            }
+            Err(..) => Err(self.set_error("Failed to parse integer", position)),
+        }
+    }
+
+    // Parsing a range is a parser on it's own right. Here is the grammar:
+    //
+    //    range       -> cell | cell ':' cell | row ':' row | column ':' column
+    //    cell        -> column row
+    //    column      -> '$' column_name | column_name
+    //    row         -> '$' row_name | row_name
+    //    column_name -> 'A'..'XFD'
+    //    row_name    -> 1..1_048_576
+    pub(super) fn consume_range_a1(&mut self) -> Result<ParsedRange> {
+        // first let's try to parse a cell
+        let mut position = self.position;
+        match self.consume_reference_a1() {
+            Ok(cell) => {
+                if self.peek_char() == Some(':') {
+                    // It's a range
+                    self.position += 1;
+                    if let Ok(cell2) = self.consume_reference_a1() {
+                        Ok(ParsedRange {
+                            left: cell,
+                            right: Some(cell2),
+                        })
+                    } else {
+                        Err(self.set_error("Expecting reference in range", self.position))
+                    }
+                } else {
+                    // just a reference
+                    Ok(ParsedRange {
+                        left: cell,
+                        right: None,
+                    })
+                }
+            }
+            Err(_) => {
+                self.position = position;
+                // It's either a row range or a column range (or not a range at all)
+                let len = self.len;
+                let mut absolute_left = false;
+                if position < len && self.chars[position] == '$' {
+                    absolute_left = true;
+                    position += 1;
+                }
+                let mut column_left = "".to_string();
+                let mut row_left = "".to_string();
+                while position < len {
+                    let x = self.chars[position].to_ascii_uppercase();
+                    match x {
+                        'A'..='Z' => column_left.push(x),
+                        '0'..='9' => row_left.push(x),
+                        _ => break,
+                    }
+                    position += 1;
+                }
+                if position >= len || self.chars[position] != ':' {
+                    return Err(self.set_error("Expecting reference in range", self.position));
+                }
+                position += 1;
+                let mut absolute_right = false;
+                if position < len && self.chars[position] == '$' {
+                    absolute_right = true;
+                    position += 1;
+                }
+                let mut column_right = "".to_string();
+                let mut row_right = "".to_string();
+                while position < len {
+                    let x = self.chars[position].to_ascii_uppercase();
+                    match x {
+                        'A'..='Z' => column_right.push(x),
+                        '0'..='9' => row_right.push(x),
+                        _ => break,
+                    }
+                    position += 1;
+                }
+                self.position = position;
+                // At this point either the columns are the empty string or the rows are the empty string
+                if !row_left.is_empty() {
+                    // It is a row range 23:56
+                    if row_right.is_empty() || !column_left.is_empty() || !column_right.is_empty() {
+                        return Err(self.set_error("Error parsing Range", position));
+                    }
+                    // Note that row numbers can start with 0
+                    let row_left = match row_left.parse::<i32>() {
+                        Ok(n) => n,
+                        Err(_) => {
+                            return Err(self
+                                .set_error(&format!("Failed parsing row {}", row_left), position))
+                        }
+                    };
+                    let row_right = match row_right.parse::<i32>() {
+                        Ok(n) => n,
+                        Err(_) => {
+                            return Err(self
+                                .set_error(&format!("Failed parsing row {}", row_right), position))
+                        }
+                    };
+                    if row_left > LAST_ROW {
+                        return Err(self.set_error("Row too large in reference", position));
+                    }
+                    if row_right > LAST_ROW {
+                        return Err(self.set_error("Row too large in reference", position));
+                    }
+                    return Ok(ParsedRange {
+                        left: ParsedReference {
+                            row: row_left,
+                            absolute_row: absolute_left,
+                            column: 1,
+                            absolute_column: true,
+                        },
+                        right: Some(ParsedReference {
+                            row: row_right,
+                            absolute_row: absolute_right,
+                            column: LAST_COLUMN,
+                            absolute_column: true,
+                        }),
+                    });
+                }
+                // It is a column range
+                if column_right.is_empty() || !row_right.is_empty() {
+                    return Err(self.set_error("Error parsing Range", position));
+                }
+                let column_left = column_to_number(&column_left)
+                    .map_err(|error| self.set_error(&error, position))?;
+                let column_right = column_to_number(&column_right)
+                    .map_err(|error| self.set_error(&error, position))?;
+                Ok(ParsedRange {
+                    left: ParsedReference {
+                        row: 1,
+                        absolute_row: true,
+                        column: column_left,
+                        absolute_column: absolute_left,
+                    },
+                    right: Some(ParsedReference {
+                        row: LAST_ROW,
+                        absolute_row: true,
+                        column: column_right,
+                        absolute_column: absolute_right,
+                    }),
+                })
+            }
+        }
+    }
+
+    pub(super) fn consume_range_r1c1(&mut self) -> Result<ParsedRange> {
+        // first let's try to parse a cell
+        match self.consume_reference_r1c1() {
+            Ok(cell) => {
+                if self.peek_char() == Some(':') {
+                    // It's a range
+                    self.position += 1;
+                    if let Ok(cell2) = self.consume_reference_r1c1() {
+                        Ok(ParsedRange {
+                            left: cell,
+                            right: Some(cell2),
+                        })
+                    } else {
+                        Err(self.set_error("Expecting reference in range", self.position))
+                    }
+                } else {
+                    // just a reference
+                    Ok(ParsedRange {
+                        left: cell,
+                        right: None,
+                    })
+                }
+            }
+            Err(s) => Err(s),
+        }
+    }
+
+    pub(super) fn consume_reference_r1c1(&mut self) -> Result<ParsedReference> {
+        // R12C3, R[2]C[-2], R3C[6], R[-3]C4, RC1, R[-2]C
+        let absolute_column;
+        let absolute_row;
+        let position = self.position;
+        let row;
+        let column;
+        self.expect_char('R')?;
+        match self.peek_char() {
+            Some('[') => {
+                absolute_row = false;
+                self.expect_char('[')?;
+                let c = match self.read_next_char() {
+                    Some(s) => s,
+                    None => {
+                        return Err(self.set_error("Expected column number", position));
+                    }
+                };
+                match self.consume_integer(c) {
+                    Ok(v) => row = v,
+                    Err(_) => {
+                        return Err(self.set_error("Expected row number", position));
+                    }
+                }
+                self.expect(TokenType::RightBracket)?;
+            }
+            Some(c) => {
+                absolute_row = true;
+                self.expect_char(c)?;
+                match self.consume_integer(c) {
+                    Ok(v) => row = v,
+                    Err(_) => {
+                        return Err(self.set_error("Expected row number", position));
+                    }
+                }
+            }
+            None => {
+                return Err(self.set_error("Expected row number or '['", position));
+            }
+        }
+        self.expect_char('C')?;
+        match self.peek_char() {
+            Some('[') => {
+                self.expect_char('[')?;
+                absolute_column = false;
+                let c = match self.read_next_char() {
+                    Some(s) => s,
+                    None => {
+                        return Err(self.set_error("Expected column number", position));
+                    }
+                };
+                match self.consume_integer(c) {
+                    Ok(v) => column = v,
+                    Err(_) => {
+                        return Err(self.set_error("Expected column number", position));
+                    }
+                }
+                self.expect(TokenType::RightBracket)?;
+            }
+            Some(c) => {
+                absolute_column = true;
+                self.expect_char(c)?;
+                match self.consume_integer(c) {
+                    Ok(v) => column = v,
+                    Err(_) => {
+                        return Err(self.set_error("Expected column number", position));
+                    }
+                }
+            }
+            None => {
+                return Err(self.set_error("Expected column number or '['", position));
+            }
+        }
+        if let Some(c) = self.peek_char() {
+            if c.is_alphanumeric() {
+                return Err(self.set_error("Expected end of reference", position));
+            }
+        }
+
+        Ok(ParsedReference {
+            column,
+            row,
+            absolute_column,
+            absolute_row,
+        })
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/expressions/lexer/structured_references.rs.html b/src/ironcalc_base/expressions/lexer/structured_references.rs.html new file mode 100644 index 0000000..07b3092 --- /dev/null +++ b/src/ironcalc_base/expressions/lexer/structured_references.rs.html @@ -0,0 +1,377 @@ +structured_references.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+
// Grammar:
+// structured references -> table_name "[" arguments "]"
+//  arguments -> table_reference | "["specifier"]"  "," table_reference
+//  specifier > "#All"      |
+//              "#This Row" |
+//              "#Data"     |
+//              "#Headers"  |
+//              "#Totals"
+// table_reference -> column_reference | range_reference
+// column reference -> column_name | "["column_name"]"
+// range_reference -> column_reference":"column_reference
+
+use crate::expressions::token::TokenType;
+use crate::expressions::token::{TableReference, TableSpecifier};
+
+use super::Result;
+use super::{Lexer, LexerError};
+
+impl Lexer {
+    fn consume_table_specifier(&mut self) -> Result<Option<TableSpecifier>> {
+        if self.peek_char() == Some('#') {
+            // It's a specifier
+            // TODO(TD): There are better ways of doing this :)
+            let rest_of_formula: String = self.chars[self.position..self.len].iter().collect();
+            let specifier = if rest_of_formula.starts_with("#This Row]") {
+                self.position += "#This Row]".bytes().len();
+                TableSpecifier::ThisRow
+            } else if rest_of_formula.starts_with("#All]") {
+                self.position += "#All]".bytes().len();
+                TableSpecifier::All
+            } else if rest_of_formula.starts_with("#Data]") {
+                self.position += "#Data]".bytes().len();
+                TableSpecifier::Data
+            } else if rest_of_formula.starts_with("#Headers]") {
+                self.position += "#Headers]".bytes().len();
+                TableSpecifier::Headers
+            } else if rest_of_formula.starts_with("#Totals]") {
+                self.position += "#Totals]".bytes().len();
+                TableSpecifier::Totals
+            } else {
+                return Err(LexerError {
+                    position: self.position,
+                    message: "Invalid structured reference".to_string(),
+                });
+            };
+            Ok(Some(specifier))
+        } else {
+            Ok(None)
+        }
+    }
+
+    fn consume_column_reference(&mut self) -> Result<String> {
+        self.consume_whitespace();
+        let end_char = if self.peek_char() == Some('[') {
+            self.position += 1;
+            ']'
+        } else {
+            ')'
+        };
+
+        let mut position = self.position;
+        while position < self.len {
+            let next_char = self.chars[position];
+            if next_char != end_char {
+                position += 1;
+                if next_char == '\'' {
+                    if position == self.len {
+                        return Err(LexerError {
+                            position: self.position,
+                            message: "Invalid column name".to_string(),
+                        });
+                    }
+                    // skip next char
+                    position += 1
+                }
+            } else {
+                break;
+            }
+        }
+        let chars: String = self.chars[self.position..position].iter().collect();
+        if end_char == ']' {
+            position += 1;
+        }
+        self.position = position;
+        Ok(chars
+            .replace("'[", "[")
+            .replace("']", "]")
+            .replace("'#", "#")
+            .replace("'@", "@")
+            .replace("''", "'"))
+    }
+
+    // Possibilities:
+    //  1. MyTable[#Totals] or MyTable[#This Row]
+    //  2. MyTable[MyColumn]
+    //  3. MyTable[[My Column]]
+    //  4. MyTable[[#This Row], [My Column]]
+    //  5. MyTable[[#Totals], [MyColumn]]
+    //  6. MyTable[[#This Row], [Jan]:[Dec]]
+    //  7. MyTable[]
+    //
+    // Multiple specifiers are not supported yet:
+    //  1. MyTable[[#Data], [#Totals], [MyColumn]]
+    //
+    // In particular note that names of columns are escaped only when they are in the first argument
+    // We use '[' and ']'
+    // When there is only a specifier but not a reference the specifier is not in brackets
+    //
+    // Invalid:
+    // * MyTable[#Totals, [Jan]:[March]] => MyTable[[#Totals], [Jan]:[March]]
+    //
+    // NOTES:
+    // * MyTable[[#Totals]] is translated into MyTable[#Totals]
+    // * Excel shows '@' instead of '#This Row':
+    //     MyTable[[#This Row], [Jan]:[Dec]] => MyTable[@[Jan]:[Dec]]
+    //   But this is only a UI thing that we will ignore for now.
+    pub(crate) fn consume_structured_reference(&mut self, table_name: &str) -> Result<TokenType> {
+        self.expect(TokenType::LeftBracket)?;
+        let peek_char = self.peek_char();
+        if peek_char == Some(']') {
+            // This is just a reference to the full table
+            self.expect(TokenType::RightBracket)?;
+            return Ok(TokenType::Ident(table_name.to_string()));
+        }
+        if peek_char == Some('#') {
+            // Expecting MyTable[#Totals]
+            if let Some(specifier) = self.consume_table_specifier()? {
+                return Ok(TokenType::StructuredReference {
+                    table_name: table_name.to_string(),
+                    specifier: Some(specifier),
+                    table_reference: None,
+                });
+            } else {
+                return Err(LexerError {
+                    position: self.position,
+                    message: "Invalid structured reference".to_string(),
+                });
+            }
+        } else if peek_char != Some('[') {
+            // Expecting MyTable[MyColumn]
+            self.position -= 1;
+            let column_name = self.consume_column_reference()?;
+            return Ok(TokenType::StructuredReference {
+                table_name: table_name.to_string(),
+                specifier: None,
+                table_reference: Some(TableReference::ColumnReference(column_name)),
+            });
+        }
+        self.expect(TokenType::LeftBracket)?;
+        let specifier = self.consume_table_specifier()?;
+        if specifier.is_some() {
+            let peek_token = self.peek_token();
+            if peek_token == TokenType::Comma {
+                self.advance_token();
+                self.expect(TokenType::LeftBracket)?;
+            } else if peek_token == TokenType::RightBracket {
+                return Ok(TokenType::StructuredReference {
+                    table_name: table_name.to_string(),
+                    specifier,
+                    table_reference: None,
+                });
+            }
+        }
+
+        // Now it's either:
+        // [Column Name]
+        // [Column Name]:[Column Name]
+        self.position -= 1;
+        let column_reference = self.consume_column_reference()?;
+        let table_reference = if self.peek_char() == Some(':') {
+            self.position += 1;
+            let column_reference_right = self.consume_column_reference()?;
+            self.expect(TokenType::RightBracket)?;
+            Some(TableReference::RangeReference((
+                column_reference,
+                column_reference_right,
+            )))
+        } else {
+            self.expect(TokenType::RightBracket)?;
+            Some(TableReference::ColumnReference(column_reference))
+        };
+        Ok(TokenType::StructuredReference {
+            table_name: table_name.to_string(),
+            specifier,
+            table_reference,
+        })
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/expressions/lexer/util.rs.html b/src/ironcalc_base/expressions/lexer/util.rs.html new file mode 100644 index 0000000..d394853 --- /dev/null +++ b/src/ironcalc_base/expressions/lexer/util.rs.html @@ -0,0 +1,171 @@ +util.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+
use std::fmt;
+
+use crate::expressions::token;
+use crate::language::get_language;
+use crate::locale::get_locale;
+
+use super::{Lexer, LexerMode};
+
+/// A MarkedToken is a token together with its position on a formula
+#[derive(Debug, PartialEq)]
+pub struct MarkedToken {
+    pub token: token::TokenType,
+    pub start: i32,
+    pub end: i32,
+}
+
+/// Returns a list of marked tokens for a formula
+///
+/// # Examples
+/// ```
+/// use ironcalc_base::expressions::{
+///      lexer::util::{get_tokens, MarkedToken},
+///      token::{OpSum, TokenType},
+/// };
+///
+/// let marked_tokens = get_tokens("A1+1");
+/// let first_t = MarkedToken {
+///     token: TokenType::Reference {
+///         sheet: None,
+///         row: 1,
+///         column: 1,
+///         absolute_column: false,
+///         absolute_row: false,
+///     },
+///     start: 0,
+///     end: 2,
+/// };
+/// let second_t = MarkedToken {
+///     token: TokenType::Addition(OpSum::Add),
+///     start:2,
+///     end: 3
+/// };
+/// let third_t = MarkedToken {
+///     token: TokenType::Number(1.0),
+///     start:3,
+///     end: 4
+/// };
+/// assert_eq!(marked_tokens, vec![first_t, second_t, third_t]);
+/// ```
+pub fn get_tokens(formula: &str) -> Vec<MarkedToken> {
+    let mut tokens = Vec::new();
+    let mut lexer = Lexer::new(
+        formula,
+        LexerMode::A1,
+        get_locale("en").expect(""),
+        get_language("en").expect(""),
+    );
+    let mut start = lexer.get_position();
+    let mut next_token = lexer.next_token();
+    let mut end = lexer.get_position();
+    loop {
+        match next_token {
+            token::TokenType::EOF => {
+                break;
+            }
+            _ => {
+                tokens.push(MarkedToken {
+                    start,
+                    end,
+                    token: next_token,
+                });
+                start = lexer.get_position();
+                next_token = lexer.next_token();
+                end = lexer.get_position();
+            }
+        }
+    }
+    tokens
+}
+
+impl fmt::Display for MarkedToken {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(fmt, "{}", self.token)
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/expressions/mod.rs.html b/src/ironcalc_base/expressions/mod.rs.html new file mode 100644 index 0000000..2cd346c --- /dev/null +++ b/src/ironcalc_base/expressions/mod.rs.html @@ -0,0 +1,13 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+
// public modules
+pub mod lexer;
+pub mod parser;
+pub mod token;
+pub mod types;
+pub mod utils;
+
\ No newline at end of file diff --git a/src/ironcalc_base/expressions/parser/mod.rs.html b/src/ironcalc_base/expressions/parser/mod.rs.html new file mode 100644 index 0000000..3b1c9af --- /dev/null +++ b/src/ironcalc_base/expressions/parser/mod.rs.html @@ -0,0 +1,1755 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+
/*!
+# GRAMAR
+
+<pre class="rust">
+opComp   => '=' | '<' | '>' | '<=' } '>=' | '<>'
+opFactor => '*' | '/'
+unaryOp  => '-' | '+'
+
+expr    => concat (opComp concat)*
+concat  => term ('&' term)*
+term    => factor (opFactor factor)*
+factor  => prod (opProd prod)*
+prod    => power ('^' power)*
+power   => (unaryOp)* range '%'*
+range   => primary (':' primary)?
+primary => '(' expr ')'
+        => number
+        => function '(' f_args ')'
+        => name
+        => string
+        => '{' a_args '}'
+        => bool
+        => bool()
+        => error
+
+f_args  => e (',' e)*
+</pre>
+*/
+
+use std::collections::HashMap;
+
+use crate::functions::Function;
+use crate::language::get_language;
+use crate::locale::get_locale;
+use crate::types::Table;
+
+use super::lexer;
+use super::token;
+use super::token::OpUnary;
+use super::token::TableReference;
+use super::token::TokenType;
+use super::types::*;
+use super::utils::number_to_column;
+
+use token::OpCompare;
+
+pub mod move_formula;
+pub mod stringify;
+pub mod walk;
+
+#[cfg(test)]
+mod test;
+
+#[cfg(test)]
+mod test_ranges;
+
+#[cfg(test)]
+mod test_move_formula;
+#[cfg(test)]
+mod test_tables;
+
+pub(crate) fn parse_range(formula: &str) -> Result<(i32, i32, i32, i32), String> {
+    let mut lexer = lexer::Lexer::new(
+        formula,
+        lexer::LexerMode::A1,
+        get_locale("en").expect(""),
+        get_language("en").expect(""),
+    );
+    if let TokenType::Range {
+        left,
+        right,
+        sheet: _,
+    } = lexer.next_token()
+    {
+        Ok((left.column, left.row, right.column, right.row))
+    } else {
+        Err("Not a range".to_string())
+    }
+}
+
+fn get_table_column_by_name(table_column_name: &str, table: &Table) -> Option<i32> {
+    for (index, table_column) in table.columns.iter().enumerate() {
+        if table_column.name == table_column_name {
+            return Some(index as i32);
+        }
+    }
+    None
+}
+
+pub(crate) struct Reference<'a> {
+    sheet_name: &'a Option<String>,
+    sheet_index: u32,
+    absolute_row: bool,
+    absolute_column: bool,
+    row: i32,
+    column: i32,
+}
+
+#[derive(PartialEq, Clone, Debug)]
+pub enum Node {
+    BooleanKind(bool),
+    NumberKind(f64),
+    StringKind(String),
+    ReferenceKind {
+        sheet_name: Option<String>,
+        sheet_index: u32,
+        absolute_row: bool,
+        absolute_column: bool,
+        row: i32,
+        column: i32,
+    },
+    RangeKind {
+        sheet_name: Option<String>,
+        sheet_index: u32,
+        absolute_row1: bool,
+        absolute_column1: bool,
+        row1: i32,
+        column1: i32,
+        absolute_row2: bool,
+        absolute_column2: bool,
+        row2: i32,
+        column2: i32,
+    },
+    WrongReferenceKind {
+        sheet_name: Option<String>,
+        absolute_row: bool,
+        absolute_column: bool,
+        row: i32,
+        column: i32,
+    },
+    WrongRangeKind {
+        sheet_name: Option<String>,
+        absolute_row1: bool,
+        absolute_column1: bool,
+        row1: i32,
+        column1: i32,
+        absolute_row2: bool,
+        absolute_column2: bool,
+        row2: i32,
+        column2: i32,
+    },
+    OpRangeKind {
+        left: Box<Node>,
+        right: Box<Node>,
+    },
+    OpConcatenateKind {
+        left: Box<Node>,
+        right: Box<Node>,
+    },
+    OpSumKind {
+        kind: token::OpSum,
+        left: Box<Node>,
+        right: Box<Node>,
+    },
+    OpProductKind {
+        kind: token::OpProduct,
+        left: Box<Node>,
+        right: Box<Node>,
+    },
+    OpPowerKind {
+        left: Box<Node>,
+        right: Box<Node>,
+    },
+    FunctionKind {
+        kind: Function,
+        args: Vec<Node>,
+    },
+    InvalidFunctionKind {
+        name: String,
+        args: Vec<Node>,
+    },
+    ArrayKind(Vec<Node>),
+    VariableKind(String),
+    CompareKind {
+        kind: OpCompare,
+        left: Box<Node>,
+        right: Box<Node>,
+    },
+    UnaryKind {
+        kind: OpUnary,
+        right: Box<Node>,
+    },
+    ErrorKind(token::Error),
+    ParseErrorKind {
+        formula: String,
+        message: String,
+        position: usize,
+    },
+    EmptyArgKind,
+}
+
+#[derive(Clone)]
+pub struct Parser {
+    lexer: lexer::Lexer,
+    worksheets: Vec<String>,
+    context: Option<CellReferenceRC>,
+    tables: HashMap<String, Table>,
+}
+
+impl Parser {
+    pub fn new(worksheets: Vec<String>, tables: HashMap<String, Table>) -> Parser {
+        let lexer = lexer::Lexer::new(
+            "",
+            lexer::LexerMode::A1,
+            get_locale("en").expect(""),
+            get_language("en").expect(""),
+        );
+        Parser {
+            lexer,
+            worksheets,
+            context: None,
+            tables,
+        }
+    }
+    pub fn set_lexer_mode(&mut self, mode: lexer::LexerMode) {
+        self.lexer.set_lexer_mode(mode)
+    }
+
+    pub fn set_worksheets(&mut self, worksheets: Vec<String>) {
+        self.worksheets = worksheets;
+    }
+
+    pub fn parse(&mut self, formula: &str, context: &Option<CellReferenceRC>) -> Node {
+        self.lexer.set_formula(formula);
+        self.context = context.clone();
+        self.parse_expr()
+    }
+
+    fn get_sheet_index_by_name(&self, name: &str) -> Option<u32> {
+        let worksheets = &self.worksheets;
+        for (i, sheet) in worksheets.iter().enumerate() {
+            if sheet == name {
+                return Some(i as u32);
+            }
+        }
+        None
+    }
+
+    fn parse_expr(&mut self) -> Node {
+        let mut t = self.parse_concat();
+        if let Node::ParseErrorKind { .. } = t {
+            return t;
+        }
+        let mut next_token = self.lexer.peek_token();
+        while let TokenType::Compare(op) = next_token {
+            self.lexer.advance_token();
+            let p = self.parse_concat();
+            if let Node::ParseErrorKind { .. } = p {
+                return p;
+            }
+            t = Node::CompareKind {
+                kind: op,
+                left: Box::new(t),
+                right: Box::new(p),
+            };
+            next_token = self.lexer.peek_token();
+        }
+        t
+    }
+
+    fn parse_concat(&mut self) -> Node {
+        let mut t = self.parse_term();
+        if let Node::ParseErrorKind { .. } = t {
+            return t;
+        }
+        let mut next_token = self.lexer.peek_token();
+        while next_token == TokenType::And {
+            self.lexer.advance_token();
+            let p = self.parse_term();
+            if let Node::ParseErrorKind { .. } = p {
+                return p;
+            }
+            t = Node::OpConcatenateKind {
+                left: Box::new(t),
+                right: Box::new(p),
+            };
+            next_token = self.lexer.peek_token();
+        }
+        t
+    }
+
+    fn parse_term(&mut self) -> Node {
+        let mut t = self.parse_factor();
+        if let Node::ParseErrorKind { .. } = t {
+            return t;
+        }
+        let mut next_token = self.lexer.peek_token();
+        while let TokenType::Addition(op) = next_token {
+            self.lexer.advance_token();
+            let p = self.parse_factor();
+            if let Node::ParseErrorKind { .. } = p {
+                return p;
+            }
+            t = Node::OpSumKind {
+                kind: op,
+                left: Box::new(t),
+                right: Box::new(p),
+            };
+
+            next_token = self.lexer.peek_token();
+        }
+        t
+    }
+
+    fn parse_factor(&mut self) -> Node {
+        let mut t = self.parse_prod();
+        if let Node::ParseErrorKind { .. } = t {
+            return t;
+        }
+        let mut next_token = self.lexer.peek_token();
+        while let TokenType::Product(op) = next_token {
+            self.lexer.advance_token();
+            let p = self.parse_prod();
+            if let Node::ParseErrorKind { .. } = p {
+                return p;
+            }
+            t = Node::OpProductKind {
+                kind: op,
+                left: Box::new(t),
+                right: Box::new(p),
+            };
+            next_token = self.lexer.peek_token();
+        }
+        t
+    }
+
+    fn parse_prod(&mut self) -> Node {
+        let mut t = self.parse_power();
+        if let Node::ParseErrorKind { .. } = t {
+            return t;
+        }
+        let mut next_token = self.lexer.peek_token();
+        while next_token == TokenType::Power {
+            self.lexer.advance_token();
+            let p = self.parse_power();
+            if let Node::ParseErrorKind { .. } = p {
+                return p;
+            }
+            t = Node::OpPowerKind {
+                left: Box::new(t),
+                right: Box::new(p),
+            };
+            next_token = self.lexer.peek_token();
+        }
+        t
+    }
+
+    fn parse_power(&mut self) -> Node {
+        let mut next_token = self.lexer.peek_token();
+        let mut sign = 1;
+        while let TokenType::Addition(op) = next_token {
+            self.lexer.advance_token();
+            if op == token::OpSum::Minus {
+                sign = -sign;
+            }
+            next_token = self.lexer.peek_token();
+        }
+
+        let mut t = self.parse_range();
+        if let Node::ParseErrorKind { .. } = t {
+            return t;
+        }
+        if sign == -1 {
+            t = Node::UnaryKind {
+                kind: token::OpUnary::Minus,
+                right: Box::new(t),
+            }
+        }
+        next_token = self.lexer.peek_token();
+        while next_token == TokenType::Percent {
+            self.lexer.advance_token();
+            t = Node::UnaryKind {
+                kind: token::OpUnary::Percentage,
+                right: Box::new(t),
+            };
+            next_token = self.lexer.peek_token();
+        }
+        t
+    }
+
+    fn parse_range(&mut self) -> Node {
+        let t = self.parse_primary();
+        if let Node::ParseErrorKind { .. } = t {
+            return t;
+        }
+        let next_token = self.lexer.peek_token();
+        if next_token == TokenType::Colon {
+            self.lexer.advance_token();
+            let p = self.parse_primary();
+            if let Node::ParseErrorKind { .. } = p {
+                return p;
+            }
+            return Node::OpRangeKind {
+                left: Box::new(t),
+                right: Box::new(p),
+            };
+        }
+        t
+    }
+
+    fn parse_primary(&mut self) -> Node {
+        let next_token = self.lexer.next_token();
+        match next_token {
+            TokenType::LeftParenthesis => {
+                let t = self.parse_expr();
+                if let Node::ParseErrorKind { .. } = t {
+                    return t;
+                }
+
+                if let Err(err) = self.lexer.expect(TokenType::RightParenthesis) {
+                    return Node::ParseErrorKind {
+                        formula: self.lexer.get_formula(),
+                        position: err.position,
+                        message: err.message,
+                    };
+                }
+                t
+            }
+            TokenType::Number(s) => Node::NumberKind(s),
+            TokenType::String(s) => Node::StringKind(s),
+            TokenType::LeftBrace => {
+                let t = self.parse_expr();
+                if let Node::ParseErrorKind { .. } = t {
+                    return t;
+                }
+                let mut next_token = self.lexer.peek_token();
+                let mut args: Vec<Node> = vec![t];
+                while next_token == TokenType::Semicolon {
+                    self.lexer.advance_token();
+                    let p = self.parse_expr();
+                    if let Node::ParseErrorKind { .. } = p {
+                        return p;
+                    }
+                    next_token = self.lexer.peek_token();
+                    args.push(p);
+                }
+                if let Err(err) = self.lexer.expect(TokenType::RightBrace) {
+                    return Node::ParseErrorKind {
+                        formula: self.lexer.get_formula(),
+                        position: err.position,
+                        message: err.message,
+                    };
+                }
+                Node::ArrayKind(args)
+            }
+            TokenType::Reference {
+                sheet,
+                row,
+                column,
+                absolute_column,
+                absolute_row,
+            } => {
+                let context = match &self.context {
+                    Some(c) => c,
+                    None => {
+                        return Node::ParseErrorKind {
+                            formula: self.lexer.get_formula(),
+                            position: self.lexer.get_position() as usize,
+                            message: "Expected context for the reference".to_string(),
+                        }
+                    }
+                };
+                let sheet_index = match &sheet {
+                    Some(name) => self.get_sheet_index_by_name(name),
+                    None => self.get_sheet_index_by_name(&context.sheet),
+                };
+                let a1_mode = self.lexer.is_a1_mode();
+                let row = if absolute_row || !a1_mode {
+                    row
+                } else {
+                    row - context.row
+                };
+                let column = if absolute_column || !a1_mode {
+                    column
+                } else {
+                    column - context.column
+                };
+                match sheet_index {
+                    Some(index) => Node::ReferenceKind {
+                        sheet_name: sheet,
+                        sheet_index: index,
+                        row,
+                        column,
+                        absolute_row,
+                        absolute_column,
+                    },
+                    None => Node::WrongReferenceKind {
+                        sheet_name: sheet,
+                        row,
+                        column,
+                        absolute_row,
+                        absolute_column,
+                    },
+                }
+            }
+            TokenType::Range { sheet, left, right } => {
+                let context = match &self.context {
+                    Some(c) => c,
+                    None => {
+                        return Node::ParseErrorKind {
+                            formula: self.lexer.get_formula(),
+                            position: self.lexer.get_position() as usize,
+                            message: "Expected context for the reference".to_string(),
+                        }
+                    }
+                };
+                let sheet_index = match &sheet {
+                    Some(name) => self.get_sheet_index_by_name(name),
+                    None => self.get_sheet_index_by_name(&context.sheet),
+                };
+                let mut row1 = left.row;
+                let mut column1 = left.column;
+                let mut row2 = right.row;
+                let mut column2 = right.column;
+
+                let mut absolute_column1 = left.absolute_column;
+                let mut absolute_column2 = right.absolute_column;
+                let mut absolute_row1 = left.absolute_row;
+                let mut absolute_row2 = right.absolute_row;
+
+                if self.lexer.is_a1_mode() {
+                    if !left.absolute_row {
+                        row1 -= context.row
+                    };
+                    if !left.absolute_column {
+                        column1 -= context.column
+                    };
+                    if !right.absolute_row {
+                        row2 -= context.row
+                    };
+                    if !right.absolute_column {
+                        column2 -= context.column
+                    };
+                }
+                if row1 > row2 {
+                    (row2, row1) = (row1, row2);
+                    (absolute_row2, absolute_row1) = (absolute_row1, absolute_row2);
+                }
+                if column1 > column2 {
+                    (column2, column1) = (column1, column2);
+                    (absolute_column2, absolute_column1) = (absolute_column1, absolute_column2);
+                }
+                match sheet_index {
+                    Some(index) => Node::RangeKind {
+                        sheet_name: sheet,
+                        sheet_index: index,
+                        row1,
+                        column1,
+                        row2,
+                        column2,
+                        absolute_column1,
+                        absolute_column2,
+                        absolute_row1,
+                        absolute_row2,
+                    },
+                    None => Node::WrongRangeKind {
+                        sheet_name: sheet,
+                        row1,
+                        column1,
+                        row2,
+                        column2,
+                        absolute_column1,
+                        absolute_column2,
+                        absolute_row1,
+                        absolute_row2,
+                    },
+                }
+            }
+            TokenType::Ident(name) => {
+                let next_token = self.lexer.peek_token();
+                if next_token == TokenType::LeftParenthesis {
+                    // It's a function call "SUM(.."
+                    self.lexer.advance_token();
+                    let args = match self.parse_function_args() {
+                        Ok(s) => s,
+                        Err(e) => return e,
+                    };
+                    if let Err(err) = self.lexer.expect(TokenType::RightParenthesis) {
+                        return Node::ParseErrorKind {
+                            formula: self.lexer.get_formula(),
+                            position: err.position,
+                            message: err.message,
+                        };
+                    }
+                    if let Some(function_kind) = Function::get_function(&name) {
+                        return Node::FunctionKind {
+                            kind: function_kind,
+                            args,
+                        };
+                    } else {
+                        return Node::InvalidFunctionKind { name, args };
+                    }
+                }
+                Node::VariableKind(name)
+            }
+            TokenType::Error(kind) => Node::ErrorKind(kind),
+            TokenType::Illegal(error) => Node::ParseErrorKind {
+                formula: self.lexer.get_formula(),
+                position: error.position,
+                message: error.message,
+            },
+            TokenType::EOF => Node::ParseErrorKind {
+                formula: self.lexer.get_formula(),
+                position: 0,
+                message: "Unexpected end of input.".to_string(),
+            },
+            TokenType::Boolean(value) => Node::BooleanKind(value),
+            TokenType::Compare(_) => {
+                // A primary Node cannot start with an operator
+                Node::ParseErrorKind {
+                    formula: self.lexer.get_formula(),
+                    position: 0,
+                    message: "Unexpected token: 'COMPARE'".to_string(),
+                }
+            }
+            TokenType::Addition(_) => {
+                // A primary Node cannot start with an operator
+                Node::ParseErrorKind {
+                    formula: self.lexer.get_formula(),
+                    position: 0,
+                    message: "Unexpected token: 'SUM'".to_string(),
+                }
+            }
+            TokenType::Product(_) => {
+                // A primary Node cannot start with an operator
+                Node::ParseErrorKind {
+                    formula: self.lexer.get_formula(),
+                    position: 0,
+                    message: "Unexpected token: 'PRODUCT'".to_string(),
+                }
+            }
+            TokenType::Power => {
+                // A primary Node cannot start with an operator
+                Node::ParseErrorKind {
+                    formula: self.lexer.get_formula(),
+                    position: 0,
+                    message: "Unexpected token: 'POWER'".to_string(),
+                }
+            }
+            TokenType::RightParenthesis
+            | TokenType::RightBracket
+            | TokenType::Colon
+            | TokenType::Semicolon
+            | TokenType::RightBrace
+            | TokenType::Comma
+            | TokenType::Bang
+            | TokenType::And
+            | TokenType::Percent => Node::ParseErrorKind {
+                formula: self.lexer.get_formula(),
+                position: 0,
+                message: format!("Unexpected token: '{}'", next_token),
+            },
+            TokenType::LeftBracket => Node::ParseErrorKind {
+                formula: self.lexer.get_formula(),
+                position: 0,
+                message: "Unexpected token: '['".to_string(),
+            },
+            TokenType::StructuredReference {
+                table_name,
+                specifier,
+                table_reference,
+            } => {
+                // We will try to convert to a normal reference
+                // table_name[column_name] => cell1:cell2
+                // table_name[[#This Row], [column_name]:[column_name]] => cell1:cell2
+                if let Some(context) = &self.context {
+                    let context_sheet_index = match self.get_sheet_index_by_name(&context.sheet) {
+                        Some(i) => i,
+                        None => {
+                            return Node::ParseErrorKind {
+                                formula: self.lexer.get_formula(),
+                                position: 0,
+                                message: "sheet not found".to_string(),
+                            };
+                        }
+                    };
+                    // table-name => table
+                    let table = self.tables.get(&table_name).unwrap_or_else(|| {
+                        panic!(
+                            "Table not found: '{table_name}' at '{}!{}{}'",
+                            context.sheet,
+                            number_to_column(context.column).expect(""),
+                            context.row
+                        )
+                    });
+                    let table_sheet_index = match self.get_sheet_index_by_name(&table.sheet_name) {
+                        Some(i) => i,
+                        None => {
+                            return Node::ParseErrorKind {
+                                formula: self.lexer.get_formula(),
+                                position: 0,
+                                message: "sheet not found".to_string(),
+                            };
+                        }
+                    };
+
+                    let sheet_name = if table_sheet_index == context_sheet_index {
+                        None
+                    } else {
+                        Some(table.sheet_name.clone())
+                    };
+
+                    // context must be with tables.reference
+                    let (column_start, mut row_start, column_end, mut row_end) =
+                        parse_range(&table.reference).expect("Failed parsing range");
+
+                    let totals_row_count = table.totals_row_count as i32;
+                    let header_row_count = table.header_row_count as i32;
+                    row_end -= totals_row_count;
+
+                    match specifier {
+                        Some(token::TableSpecifier::ThisRow) => {
+                            row_start = context.row;
+                            row_end = context.row;
+                        }
+                        Some(token::TableSpecifier::Totals) => {
+                            if totals_row_count != 0 {
+                                row_start = row_end + 1;
+                                row_end = row_start;
+                            } else {
+                                // Table1[#Totals] is #REF! if Table1 does not have totals
+                                return Node::ErrorKind(token::Error::REF);
+                            }
+                        }
+                        Some(token::TableSpecifier::Headers) => {
+                            row_end = row_start;
+                        }
+                        Some(token::TableSpecifier::Data) => {
+                            row_start += header_row_count;
+                        }
+                        Some(token::TableSpecifier::All) => {
+                            if totals_row_count != 0 {
+                                row_end += 1;
+                            }
+                        }
+                        None => {
+                            // skip the headers
+                            row_start += header_row_count;
+                        }
+                    }
+                    match table_reference {
+                        None => {
+                            return Node::RangeKind {
+                                sheet_name,
+                                sheet_index: table_sheet_index,
+                                absolute_row1: true,
+                                absolute_column1: true,
+                                row1: row_start,
+                                column1: column_start,
+                                absolute_row2: true,
+                                absolute_column2: true,
+                                row2: row_end,
+                                column2: column_end,
+                            };
+                        }
+                        Some(TableReference::ColumnReference(s)) => {
+                            let column_index = match get_table_column_by_name(&s, table) {
+                                Some(s) => s + column_start,
+                                None => {
+                                    return Node::ParseErrorKind {
+                                        formula: self.lexer.get_formula(),
+                                        position: self.lexer.get_position() as usize,
+                                        message: format!(
+                                            "Expecting column: {s} in table {table_name}"
+                                        ),
+                                    };
+                                }
+                            };
+                            if row_start == row_end {
+                                return Node::ReferenceKind {
+                                    sheet_name,
+                                    sheet_index: table_sheet_index,
+                                    absolute_row: true,
+                                    absolute_column: true,
+                                    row: row_start,
+                                    column: column_index,
+                                };
+                            }
+                            return Node::RangeKind {
+                                sheet_name,
+                                sheet_index: table_sheet_index,
+                                absolute_row1: true,
+                                absolute_column1: true,
+                                row1: row_start,
+                                column1: column_index,
+                                absolute_row2: true,
+                                absolute_column2: true,
+                                row2: row_end,
+                                column2: column_index,
+                            };
+                        }
+                        Some(TableReference::RangeReference((left, right))) => {
+                            let left_column_index = match get_table_column_by_name(&left, table) {
+                                Some(f) => f + column_start,
+                                None => {
+                                    return Node::ParseErrorKind {
+                                        formula: self.lexer.get_formula(),
+                                        position: self.lexer.get_position() as usize,
+                                        message: format!(
+                                            "Expecting column: {left} in table {table_name}"
+                                        ),
+                                    };
+                                }
+                            };
+
+                            let right_column_index = match get_table_column_by_name(&right, table) {
+                                Some(f) => f + column_start,
+                                None => {
+                                    return Node::ParseErrorKind {
+                                        formula: self.lexer.get_formula(),
+                                        position: self.lexer.get_position() as usize,
+                                        message: format!(
+                                            "Expecting column: {right} in table {table_name}"
+                                        ),
+                                    };
+                                }
+                            };
+                            return Node::RangeKind {
+                                sheet_name,
+                                sheet_index: table_sheet_index,
+                                absolute_row1: true,
+                                absolute_column1: true,
+                                row1: row_start,
+                                column1: left_column_index,
+                                absolute_row2: true,
+                                absolute_column2: true,
+                                row2: row_end,
+                                column2: right_column_index,
+                            };
+                        }
+                    }
+                }
+                Node::ParseErrorKind {
+                    formula: self.lexer.get_formula(),
+                    position: 0,
+                    message: "Structured references not supported in R1C1 mode".to_string(),
+                }
+            }
+        }
+    }
+
+    fn parse_function_args(&mut self) -> Result<Vec<Node>, Node> {
+        let mut args: Vec<Node> = Vec::new();
+        let mut next_token = self.lexer.peek_token();
+        if next_token == TokenType::RightParenthesis {
+            return Ok(args);
+        }
+        if self.lexer.peek_token() == TokenType::Comma {
+            args.push(Node::EmptyArgKind);
+        } else {
+            let t = self.parse_expr();
+            if let Node::ParseErrorKind { .. } = t {
+                return Err(t);
+            }
+            args.push(t);
+        }
+        next_token = self.lexer.peek_token();
+        while next_token == TokenType::Comma {
+            self.lexer.advance_token();
+            if self.lexer.peek_token() == TokenType::Comma {
+                args.push(Node::EmptyArgKind);
+                next_token = TokenType::Comma;
+            } else if self.lexer.peek_token() == TokenType::RightParenthesis {
+                args.push(Node::EmptyArgKind);
+                return Ok(args);
+            } else {
+                let p = self.parse_expr();
+                if let Node::ParseErrorKind { .. } = p {
+                    return Err(p);
+                }
+                next_token = self.lexer.peek_token();
+                args.push(p);
+            }
+        }
+        Ok(args)
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/expressions/parser/move_formula.rs.html b/src/ironcalc_base/expressions/parser/move_formula.rs.html new file mode 100644 index 0000000..beb88f8 --- /dev/null +++ b/src/ironcalc_base/expressions/parser/move_formula.rs.html @@ -0,0 +1,795 @@ +move_formula.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+
use super::{
+    stringify::{stringify_reference, DisplaceData},
+    Node, Reference,
+};
+use crate::{
+    constants::{LAST_COLUMN, LAST_ROW},
+    expressions::token::OpUnary,
+};
+use crate::{
+    expressions::types::{Area, CellReferenceRC},
+    number_format::to_excel_precision_str,
+};
+
+pub(crate) fn ref_is_in_area(sheet: u32, row: i32, column: i32, area: &Area) -> bool {
+    if area.sheet != sheet {
+        return false;
+    }
+    if row < area.row || row > area.row + area.height - 1 {
+        return false;
+    }
+    if column < area.column || column > area.column + area.width - 1 {
+        return false;
+    }
+    true
+}
+
+pub(crate) struct MoveContext<'a> {
+    pub source_sheet_name: &'a str,
+    pub row: i32,
+    pub column: i32,
+    pub area: &'a Area,
+    pub target_sheet_name: &'a str,
+    pub row_delta: i32,
+    pub column_delta: i32,
+}
+
+/// This implements Excel's cut && paste
+/// We are moving a formula in (row, column) to (row+row_delta, column + column_delta).
+/// All references that do not point to a cell in area will be left untouched.
+/// All references that point to a cell in area will be displaced
+pub(crate) fn move_formula(node: &Node, move_context: &MoveContext) -> String {
+    to_string_moved(node, move_context)
+}
+
+fn move_function(name: &str, args: &Vec<Node>, move_context: &MoveContext) -> String {
+    let mut first = true;
+    let mut arguments = "".to_string();
+    for el in args {
+        if !first {
+            arguments = format!("{},{}", arguments, to_string_moved(el, move_context));
+        } else {
+            first = false;
+            arguments = to_string_moved(el, move_context);
+        }
+    }
+    format!("{}({})", name, arguments)
+}
+
+fn to_string_moved(node: &Node, move_context: &MoveContext) -> String {
+    use self::Node::*;
+    match node {
+        BooleanKind(value) => format!("{}", value).to_ascii_uppercase(),
+        NumberKind(number) => to_excel_precision_str(*number),
+        StringKind(value) => format!("\"{}\"", value),
+        ReferenceKind {
+            sheet_name,
+            sheet_index,
+            absolute_row,
+            absolute_column,
+            row,
+            column,
+        } => {
+            let reference_row = if *absolute_row {
+                *row
+            } else {
+                row + move_context.row
+            };
+            let reference_column = if *absolute_column {
+                *column
+            } else {
+                column + move_context.column
+            };
+
+            let new_row;
+            let new_column;
+            let mut ref_sheet_name = sheet_name;
+            let source_sheet_name = &Some(move_context.source_sheet_name.to_string());
+
+            if ref_is_in_area(
+                *sheet_index,
+                reference_row,
+                reference_column,
+                move_context.area,
+            ) {
+                // if the reference is in the area we are moving we want to displace the reference
+                new_row = row + move_context.row_delta;
+                new_column = column + move_context.column_delta;
+            } else {
+                // If the reference is not in the area we are moving the reference remains unchanged
+                new_row = *row;
+                new_column = *column;
+                if move_context.target_sheet_name != move_context.source_sheet_name
+                    && sheet_name.is_none()
+                {
+                    ref_sheet_name = source_sheet_name;
+                }
+            };
+            let context = CellReferenceRC {
+                sheet: move_context.source_sheet_name.to_string(),
+                column: move_context.column,
+                row: move_context.row,
+            };
+            stringify_reference(
+                Some(&context),
+                &DisplaceData::None,
+                &Reference {
+                    sheet_name: ref_sheet_name,
+                    sheet_index: *sheet_index,
+                    absolute_row: *absolute_row,
+                    absolute_column: *absolute_column,
+                    row: new_row,
+                    column: new_column,
+                },
+                false,
+                false,
+            )
+        }
+        RangeKind {
+            sheet_name,
+            sheet_index,
+            absolute_row1,
+            absolute_column1,
+            row1,
+            column1,
+            absolute_row2,
+            absolute_column2,
+            row2,
+            column2,
+        } => {
+            let full_row = *absolute_row1 && *absolute_row2 && (*row1 == 1) && (*row2 == LAST_ROW);
+            let full_column = *absolute_column1
+                && *absolute_column2
+                && (*column1 == 1)
+                && (*column2 == LAST_COLUMN);
+
+            let reference_row1 = if *absolute_row1 {
+                *row1
+            } else {
+                row1 + move_context.row
+            };
+            let reference_column1 = if *absolute_column1 {
+                *column1
+            } else {
+                column1 + move_context.column
+            };
+
+            let reference_row2 = if *absolute_row2 {
+                *row2
+            } else {
+                row2 + move_context.row
+            };
+            let reference_column2 = if *absolute_column2 {
+                *column2
+            } else {
+                column2 + move_context.column
+            };
+
+            let new_row1;
+            let new_column1;
+            let new_row2;
+            let new_column2;
+            let mut ref_sheet_name = sheet_name;
+            let source_sheet_name = &Some(move_context.source_sheet_name.to_string());
+            if ref_is_in_area(
+                *sheet_index,
+                reference_row1,
+                reference_column1,
+                move_context.area,
+            ) && ref_is_in_area(
+                *sheet_index,
+                reference_row2,
+                reference_column2,
+                move_context.area,
+            ) {
+                // if the whole range is inside the area we are moving we want to displace the context
+                new_row1 = row1 + move_context.row_delta;
+                new_column1 = column1 + move_context.column_delta;
+                new_row2 = row2 + move_context.row_delta;
+                new_column2 = column2 + move_context.column_delta;
+            } else {
+                // If the reference is not in the area we are moving the context remains unchanged
+                new_row1 = *row1;
+                new_column1 = *column1;
+                new_row2 = *row2;
+                new_column2 = *column2;
+                if move_context.target_sheet_name != move_context.source_sheet_name
+                    && sheet_name.is_none()
+                {
+                    ref_sheet_name = source_sheet_name;
+                }
+            };
+            let context = CellReferenceRC {
+                sheet: move_context.source_sheet_name.to_string(),
+                column: move_context.column,
+                row: move_context.row,
+            };
+            let s1 = stringify_reference(
+                Some(&context),
+                &DisplaceData::None,
+                &Reference {
+                    sheet_name: ref_sheet_name,
+                    sheet_index: *sheet_index,
+                    absolute_row: *absolute_row1,
+                    absolute_column: *absolute_column1,
+                    row: new_row1,
+                    column: new_column1,
+                },
+                full_row,
+                full_column,
+            );
+            let s2 = stringify_reference(
+                Some(&context),
+                &DisplaceData::None,
+                &Reference {
+                    sheet_name: &None,
+                    sheet_index: *sheet_index,
+                    absolute_row: *absolute_row2,
+                    absolute_column: *absolute_column2,
+                    row: new_row2,
+                    column: new_column2,
+                },
+                full_row,
+                full_column,
+            );
+            format!("{}:{}", s1, s2)
+        }
+        WrongReferenceKind {
+            sheet_name,
+            absolute_row,
+            absolute_column,
+            row,
+            column,
+        } => {
+            // NB: Excel does not displace wrong references but Google Docs does. We follow Excel
+            let context = CellReferenceRC {
+                sheet: move_context.source_sheet_name.to_string(),
+                column: move_context.column,
+                row: move_context.row,
+            };
+            // It's a wrong reference, so there is no valid `sheet_index`.
+            // We don't need it, since the `sheet_index` is only used if `displace_data` is not `None`.
+            // I should fix it, maybe putting the `sheet_index` inside the `displace_data`
+            stringify_reference(
+                Some(&context),
+                &DisplaceData::None,
+                &Reference {
+                    sheet_name,
+                    sheet_index: 0, // HACK
+                    row: *row,
+                    column: *column,
+                    absolute_row: *absolute_row,
+                    absolute_column: *absolute_column,
+                },
+                false,
+                false,
+            )
+        }
+        WrongRangeKind {
+            sheet_name,
+            absolute_row1,
+            absolute_column1,
+            row1,
+            column1,
+            absolute_row2,
+            absolute_column2,
+            row2,
+            column2,
+        } => {
+            let full_row = *absolute_row1 && *absolute_row2 && (*row1 == 1) && (*row2 == LAST_ROW);
+            let full_column = *absolute_column1
+                && *absolute_column2
+                && (*column1 == 1)
+                && (*column2 == LAST_COLUMN);
+
+            // NB: Excel does not displace wrong references but Google Docs does. We follow Excel
+            let context = CellReferenceRC {
+                sheet: move_context.source_sheet_name.to_string(),
+                column: move_context.column,
+                row: move_context.row,
+            };
+            let s1 = stringify_reference(
+                Some(&context),
+                &DisplaceData::None,
+                &Reference {
+                    sheet_name,
+                    sheet_index: 0, // HACK
+                    row: *row1,
+                    column: *column1,
+                    absolute_row: *absolute_row1,
+                    absolute_column: *absolute_column1,
+                },
+                full_row,
+                full_column,
+            );
+            let s2 = stringify_reference(
+                Some(&context),
+                &DisplaceData::None,
+                &Reference {
+                    sheet_name: &None,
+                    sheet_index: 0, // HACK
+                    row: *row2,
+                    column: *column2,
+                    absolute_row: *absolute_row2,
+                    absolute_column: *absolute_column2,
+                },
+                full_row,
+                full_column,
+            );
+            format!("{}:{}", s1, s2)
+        }
+        OpRangeKind { left, right } => format!(
+            "{}:{}",
+            to_string_moved(left, move_context),
+            to_string_moved(right, move_context),
+        ),
+        OpConcatenateKind { left, right } => format!(
+            "{}&{}",
+            to_string_moved(left, move_context),
+            to_string_moved(right, move_context),
+        ),
+        OpSumKind { kind, left, right } => format!(
+            "{}{}{}",
+            to_string_moved(left, move_context),
+            kind,
+            to_string_moved(right, move_context),
+        ),
+        OpProductKind { kind, left, right } => {
+            let x = match **left {
+                OpSumKind { .. } => format!("({})", to_string_moved(left, move_context)),
+                CompareKind { .. } => format!("({})", to_string_moved(left, move_context)),
+                _ => to_string_moved(left, move_context),
+            };
+            let y = match **right {
+                OpSumKind { .. } => format!("({})", to_string_moved(right, move_context)),
+                CompareKind { .. } => format!("({})", to_string_moved(right, move_context)),
+                OpProductKind { .. } => format!("({})", to_string_moved(right, move_context)),
+                UnaryKind { .. } => {
+                    format!("({})", to_string_moved(right, move_context))
+                }
+                _ => to_string_moved(right, move_context),
+            };
+            format!("{}{}{}", x, kind, y)
+        }
+        OpPowerKind { left, right } => format!(
+            "{}^{}",
+            to_string_moved(left, move_context),
+            to_string_moved(right, move_context),
+        ),
+        InvalidFunctionKind { name, args } => move_function(name, args, move_context),
+        FunctionKind { kind, args } => {
+            let name = &kind.to_string();
+            move_function(name, args, move_context)
+        }
+        ArrayKind(args) => {
+            // This code is a placeholder. Arrays are not yet implemented
+            let mut first = true;
+            let mut arguments = "".to_string();
+            for el in args {
+                if !first {
+                    arguments = format!("{},{}", arguments, to_string_moved(el, move_context));
+                } else {
+                    first = false;
+                    arguments = to_string_moved(el, move_context);
+                }
+            }
+            format!("{{{}}}", arguments)
+        }
+        VariableKind(value) => value.to_string(),
+        CompareKind { kind, left, right } => format!(
+            "{}{}{}",
+            to_string_moved(left, move_context),
+            kind,
+            to_string_moved(right, move_context),
+        ),
+        UnaryKind { kind, right } => match kind {
+            OpUnary::Minus => format!("-{}", to_string_moved(right, move_context)),
+            OpUnary::Percentage => format!("{}%", to_string_moved(right, move_context)),
+        },
+        ErrorKind(kind) => format!("{}", kind),
+        ParseErrorKind {
+            formula,
+            message: _,
+            position: _,
+        } => formula.to_string(),
+        EmptyArgKind => "".to_string(),
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/expressions/parser/stringify.rs.html b/src/ironcalc_base/expressions/parser/stringify.rs.html new file mode 100644 index 0000000..9153df5 --- /dev/null +++ b/src/ironcalc_base/expressions/parser/stringify.rs.html @@ -0,0 +1,1225 @@ +stringify.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+
use super::{super::utils::quote_name, Node, Reference};
+use crate::constants::{LAST_COLUMN, LAST_ROW};
+use crate::expressions::token::OpUnary;
+use crate::{expressions::types::CellReferenceRC, number_format::to_excel_precision_str};
+
+pub enum DisplaceData {
+    Column {
+        sheet: u32,
+        column: i32,
+        delta: i32,
+    },
+    Row {
+        sheet: u32,
+        row: i32,
+        delta: i32,
+    },
+    CellHorizontal {
+        sheet: u32,
+        row: i32,
+        column: i32,
+        delta: i32,
+    },
+    CellVertical {
+        sheet: u32,
+        row: i32,
+        column: i32,
+        delta: i32,
+    },
+    ColumnMove {
+        sheet: u32,
+        column: i32,
+        delta: i32,
+    },
+    None,
+}
+
+pub fn to_rc_format(node: &Node) -> String {
+    stringify(node, None, &DisplaceData::None, false)
+}
+
+pub fn to_string_displaced(
+    node: &Node,
+    context: &CellReferenceRC,
+    displace_data: &DisplaceData,
+) -> String {
+    stringify(node, Some(context), displace_data, false)
+}
+
+pub fn to_string(node: &Node, context: &CellReferenceRC) -> String {
+    stringify(node, Some(context), &DisplaceData::None, false)
+}
+
+pub fn to_excel_string(node: &Node, context: &CellReferenceRC) -> String {
+    stringify(node, Some(context), &DisplaceData::None, true)
+}
+
+/// Converts a local reference to a string applying some displacement if needed.
+/// It uses A1 style if context is not None. If context is None it uses R1C1 style
+/// If full_row is true then the row details will be omitted in the A1 case
+/// If full_colum is true then column details will be omitted.
+pub(crate) fn stringify_reference(
+    context: Option<&CellReferenceRC>,
+    displace_data: &DisplaceData,
+    reference: &Reference,
+    full_row: bool,
+    full_column: bool,
+) -> String {
+    let sheet_name = reference.sheet_name;
+    let sheet_index = reference.sheet_index;
+    let absolute_row = reference.absolute_row;
+    let absolute_column = reference.absolute_column;
+    let row = reference.row;
+    let column = reference.column;
+    match context {
+        Some(context) => {
+            let mut row = if absolute_row { row } else { row + context.row };
+            let mut column = if absolute_column {
+                column
+            } else {
+                column + context.column
+            };
+            match displace_data {
+                DisplaceData::Row {
+                    sheet,
+                    row: displace_row,
+                    delta,
+                } => {
+                    if sheet_index == *sheet && !full_row {
+                        if *delta < 0 {
+                            if &row >= displace_row {
+                                if row < displace_row - *delta {
+                                    return "#REF!".to_string();
+                                }
+                                row += *delta;
+                            }
+                        } else if &row >= displace_row {
+                            row += *delta;
+                        }
+                    }
+                }
+                DisplaceData::Column {
+                    sheet,
+                    column: displace_column,
+                    delta,
+                } => {
+                    if sheet_index == *sheet && !full_column {
+                        if *delta < 0 {
+                            if &column >= displace_column {
+                                if column < displace_column - *delta {
+                                    return "#REF!".to_string();
+                                }
+                                column += *delta;
+                            }
+                        } else if &column >= displace_column {
+                            column += *delta;
+                        }
+                    }
+                }
+                DisplaceData::CellHorizontal {
+                    sheet,
+                    row: displace_row,
+                    column: displace_column,
+                    delta,
+                } => {
+                    if sheet_index == *sheet && displace_row == &row {
+                        if *delta < 0 {
+                            if &column >= displace_column {
+                                if column < displace_column - *delta {
+                                    return "#REF!".to_string();
+                                }
+                                column += *delta;
+                            }
+                        } else if &column >= displace_column {
+                            column += *delta;
+                        }
+                    }
+                }
+                DisplaceData::CellVertical {
+                    sheet,
+                    row: displace_row,
+                    column: displace_column,
+                    delta,
+                } => {
+                    if sheet_index == *sheet && displace_column == &column {
+                        if *delta < 0 {
+                            if &row >= displace_row {
+                                if row < displace_row - *delta {
+                                    return "#REF!".to_string();
+                                }
+                                row += *delta;
+                            }
+                        } else if &row >= displace_row {
+                            row += *delta;
+                        }
+                    }
+                }
+                DisplaceData::ColumnMove {
+                    sheet,
+                    column: move_column,
+                    delta,
+                } => {
+                    if sheet_index == *sheet {
+                        if column == *move_column {
+                            column += *delta;
+                        } else if (*delta > 0
+                            && column > *move_column
+                            && column <= *move_column + *delta)
+                            || (*delta < 0
+                                && column < *move_column
+                                && column >= *move_column + *delta)
+                        {
+                            column -= *delta;
+                        }
+                    }
+                }
+                DisplaceData::None => {}
+            }
+            if row < 1 {
+                return "#REF!".to_string();
+            }
+            let mut row_abs = if absolute_row {
+                format!("${}", row)
+            } else {
+                format!("{}", row)
+            };
+            let column = match crate::expressions::utils::number_to_column(column) {
+                Some(s) => s,
+                None => return "#REF!".to_string(),
+            };
+            let mut col_abs = if absolute_column {
+                format!("${}", column)
+            } else {
+                column
+            };
+            if full_row {
+                row_abs = "".to_string()
+            }
+            if full_column {
+                col_abs = "".to_string()
+            }
+            match &sheet_name {
+                Some(name) => {
+                    format!("{}!{}{}", quote_name(name), col_abs, row_abs)
+                }
+                None => {
+                    format!("{}{}", col_abs, row_abs)
+                }
+            }
+        }
+        None => {
+            let row_abs = if absolute_row {
+                format!("R{}", row)
+            } else {
+                format!("R[{}]", row)
+            };
+            let col_abs = if absolute_column {
+                format!("C{}", column)
+            } else {
+                format!("C[{}]", column)
+            };
+            match &sheet_name {
+                Some(name) => {
+                    format!("{}!{}{}", quote_name(name), row_abs, col_abs)
+                }
+                None => {
+                    format!("{}{}", row_abs, col_abs)
+                }
+            }
+        }
+    }
+}
+
+fn format_function(
+    name: &str,
+    args: &Vec<Node>,
+    context: Option<&CellReferenceRC>,
+    displace_data: &DisplaceData,
+    use_original_name: bool,
+) -> String {
+    let mut first = true;
+    let mut arguments = "".to_string();
+    for el in args {
+        if !first {
+            arguments = format!(
+                "{},{}",
+                arguments,
+                stringify(el, context, displace_data, use_original_name)
+            );
+        } else {
+            first = false;
+            arguments = stringify(el, context, displace_data, use_original_name);
+        }
+    }
+    format!("{}({})", name, arguments)
+}
+
+fn stringify(
+    node: &Node,
+    context: Option<&CellReferenceRC>,
+    displace_data: &DisplaceData,
+    use_original_name: bool,
+) -> String {
+    use self::Node::*;
+    match node {
+        BooleanKind(value) => format!("{}", value).to_ascii_uppercase(),
+        NumberKind(number) => to_excel_precision_str(*number),
+        StringKind(value) => format!("\"{}\"", value),
+        WrongReferenceKind {
+            sheet_name,
+            column,
+            row,
+            absolute_row,
+            absolute_column,
+        } => stringify_reference(
+            context,
+            &DisplaceData::None,
+            &Reference {
+                sheet_name,
+                sheet_index: 0,
+                row: *row,
+                column: *column,
+                absolute_row: *absolute_row,
+                absolute_column: *absolute_column,
+            },
+            false,
+            false,
+        ),
+        ReferenceKind {
+            sheet_name,
+            sheet_index,
+            column,
+            row,
+            absolute_row,
+            absolute_column,
+        } => stringify_reference(
+            context,
+            displace_data,
+            &Reference {
+                sheet_name,
+                sheet_index: *sheet_index,
+                row: *row,
+                column: *column,
+                absolute_row: *absolute_row,
+                absolute_column: *absolute_column,
+            },
+            false,
+            false,
+        ),
+        RangeKind {
+            sheet_name,
+            sheet_index,
+            absolute_row1,
+            absolute_column1,
+            row1,
+            column1,
+            absolute_row2,
+            absolute_column2,
+            row2,
+            column2,
+        } => {
+            // Note that open ranges SUM(A:A) or SUM(1:1) will be treated as normal ranges in the R1C1 (internal) representation
+            // A:A will be R1C[0]:R1048576C[0]
+            // So when we are forming the A1 range we need to strip the irrelevant information
+            let full_row = *absolute_row1 && *absolute_row2 && (*row1 == 1) && (*row2 == LAST_ROW);
+            let full_column = *absolute_column1
+                && *absolute_column2
+                && (*column1 == 1)
+                && (*column2 == LAST_COLUMN);
+            let s1 = stringify_reference(
+                context,
+                displace_data,
+                &Reference {
+                    sheet_name,
+                    sheet_index: *sheet_index,
+                    row: *row1,
+                    column: *column1,
+                    absolute_row: *absolute_row1,
+                    absolute_column: *absolute_column1,
+                },
+                full_row,
+                full_column,
+            );
+            let s2 = stringify_reference(
+                context,
+                displace_data,
+                &Reference {
+                    sheet_name: &None,
+                    sheet_index: *sheet_index,
+                    row: *row2,
+                    column: *column2,
+                    absolute_row: *absolute_row2,
+                    absolute_column: *absolute_column2,
+                },
+                full_row,
+                full_column,
+            );
+            format!("{}:{}", s1, s2)
+        }
+        WrongRangeKind {
+            sheet_name,
+            absolute_row1,
+            absolute_column1,
+            row1,
+            column1,
+            absolute_row2,
+            absolute_column2,
+            row2,
+            column2,
+        } => {
+            // Note that open ranges SUM(A:A) or SUM(1:1) will be treated as normal ranges in the R1C1 (internal) representation
+            // A:A will be R1C[0]:R1048576C[0]
+            // So when we are forming the A1 range we need to strip the irrelevant information
+            let full_row = *absolute_row1 && *absolute_row2 && (*row1 == 1) && (*row2 == LAST_ROW);
+            let full_column = *absolute_column1
+                && *absolute_column2
+                && (*column1 == 1)
+                && (*column2 == LAST_COLUMN);
+            let s1 = stringify_reference(
+                context,
+                &DisplaceData::None,
+                &Reference {
+                    sheet_name,
+                    sheet_index: 0, // HACK
+                    row: *row1,
+                    column: *column1,
+                    absolute_row: *absolute_row1,
+                    absolute_column: *absolute_column1,
+                },
+                full_row,
+                full_column,
+            );
+            let s2 = stringify_reference(
+                context,
+                &DisplaceData::None,
+                &Reference {
+                    sheet_name: &None,
+                    sheet_index: 0, // HACK
+                    row: *row2,
+                    column: *column2,
+                    absolute_row: *absolute_row2,
+                    absolute_column: *absolute_column2,
+                },
+                full_row,
+                full_column,
+            );
+            format!("{}:{}", s1, s2)
+        }
+        OpRangeKind { left, right } => format!(
+            "{}:{}",
+            stringify(left, context, displace_data, use_original_name),
+            stringify(right, context, displace_data, use_original_name)
+        ),
+        OpConcatenateKind { left, right } => format!(
+            "{}&{}",
+            stringify(left, context, displace_data, use_original_name),
+            stringify(right, context, displace_data, use_original_name)
+        ),
+        CompareKind { kind, left, right } => format!(
+            "{}{}{}",
+            stringify(left, context, displace_data, use_original_name),
+            kind,
+            stringify(right, context, displace_data, use_original_name)
+        ),
+        OpSumKind { kind, left, right } => format!(
+            "{}{}{}",
+            stringify(left, context, displace_data, use_original_name),
+            kind,
+            stringify(right, context, displace_data, use_original_name)
+        ),
+        OpProductKind { kind, left, right } => {
+            let x = match **left {
+                OpSumKind { .. } => format!(
+                    "({})",
+                    stringify(left, context, displace_data, use_original_name)
+                ),
+                CompareKind { .. } => format!(
+                    "({})",
+                    stringify(left, context, displace_data, use_original_name)
+                ),
+                _ => stringify(left, context, displace_data, use_original_name),
+            };
+            let y = match **right {
+                OpSumKind { .. } => format!(
+                    "({})",
+                    stringify(right, context, displace_data, use_original_name)
+                ),
+                CompareKind { .. } => format!(
+                    "({})",
+                    stringify(right, context, displace_data, use_original_name)
+                ),
+                OpProductKind { .. } => format!(
+                    "({})",
+                    stringify(right, context, displace_data, use_original_name)
+                ),
+                _ => stringify(right, context, displace_data, use_original_name),
+            };
+            format!("{}{}{}", x, kind, y)
+        }
+        OpPowerKind { left, right } => format!(
+            "{}^{}",
+            stringify(left, context, displace_data, use_original_name),
+            stringify(right, context, displace_data, use_original_name)
+        ),
+        InvalidFunctionKind { name, args } => {
+            format_function(name, args, context, displace_data, use_original_name)
+        }
+        FunctionKind { kind, args } => {
+            let name = if use_original_name {
+                kind.to_xlsx_string()
+            } else {
+                kind.to_string()
+            };
+            format_function(&name, args, context, displace_data, use_original_name)
+        }
+        ArrayKind(args) => {
+            let mut first = true;
+            let mut arguments = "".to_string();
+            for el in args {
+                if !first {
+                    arguments = format!(
+                        "{},{}",
+                        arguments,
+                        stringify(el, context, displace_data, use_original_name)
+                    );
+                } else {
+                    first = false;
+                    arguments = stringify(el, context, displace_data, use_original_name);
+                }
+            }
+            format!("{{{}}}", arguments)
+        }
+        VariableKind(value) => value.to_string(),
+        UnaryKind { kind, right } => match kind {
+            OpUnary::Minus => {
+                format!(
+                    "-{}",
+                    stringify(right, context, displace_data, use_original_name)
+                )
+            }
+            OpUnary::Percentage => {
+                format!(
+                    "{}%",
+                    stringify(right, context, displace_data, use_original_name)
+                )
+            }
+        },
+        ErrorKind(kind) => format!("{}", kind),
+        ParseErrorKind {
+            formula,
+            position: _,
+            message: _,
+        } => formula.to_string(),
+        EmptyArgKind => "".to_string(),
+    }
+}
+
+pub(crate) fn rename_sheet_in_node(node: &mut Node, sheet_index: u32, new_name: &str) {
+    match node {
+        // Rename
+        Node::ReferenceKind {
+            sheet_name,
+            sheet_index: index,
+            ..
+        } => {
+            if *index == sheet_index && sheet_name.is_some() {
+                *sheet_name = Some(new_name.to_owned());
+            }
+        }
+        Node::RangeKind {
+            sheet_name,
+            sheet_index: index,
+            ..
+        } => {
+            if *index == sheet_index && sheet_name.is_some() {
+                *sheet_name = Some(new_name.to_owned());
+            }
+        }
+        Node::WrongReferenceKind { sheet_name, .. } => {
+            if let Some(name) = sheet_name {
+                if name.to_uppercase() == new_name.to_uppercase() {
+                    *sheet_name = Some(name.to_owned())
+                }
+            }
+        }
+        Node::WrongRangeKind { sheet_name, .. } => {
+            if sheet_name.is_some() {
+                *sheet_name = Some(new_name.to_owned());
+            }
+        }
+
+        // Go next level
+        Node::OpRangeKind { left, right } => {
+            rename_sheet_in_node(left, sheet_index, new_name);
+            rename_sheet_in_node(right, sheet_index, new_name);
+        }
+        Node::OpConcatenateKind { left, right } => {
+            rename_sheet_in_node(left, sheet_index, new_name);
+            rename_sheet_in_node(right, sheet_index, new_name);
+        }
+        Node::OpSumKind {
+            kind: _,
+            left,
+            right,
+        } => {
+            rename_sheet_in_node(left, sheet_index, new_name);
+            rename_sheet_in_node(right, sheet_index, new_name);
+        }
+        Node::OpProductKind {
+            kind: _,
+            left,
+            right,
+        } => {
+            rename_sheet_in_node(left, sheet_index, new_name);
+            rename_sheet_in_node(right, sheet_index, new_name);
+        }
+        Node::OpPowerKind { left, right } => {
+            rename_sheet_in_node(left, sheet_index, new_name);
+            rename_sheet_in_node(right, sheet_index, new_name);
+        }
+        Node::FunctionKind { kind: _, args } => {
+            for arg in args {
+                rename_sheet_in_node(arg, sheet_index, new_name);
+            }
+        }
+        Node::InvalidFunctionKind { name: _, args } => {
+            for arg in args {
+                rename_sheet_in_node(arg, sheet_index, new_name);
+            }
+        }
+        Node::CompareKind {
+            kind: _,
+            left,
+            right,
+        } => {
+            rename_sheet_in_node(left, sheet_index, new_name);
+            rename_sheet_in_node(right, sheet_index, new_name);
+        }
+        Node::UnaryKind { kind: _, right } => {
+            rename_sheet_in_node(right, sheet_index, new_name);
+        }
+
+        // Do nothing
+        Node::BooleanKind(_) => {}
+        Node::NumberKind(_) => {}
+        Node::StringKind(_) => {}
+        Node::ErrorKind(_) => {}
+        Node::ParseErrorKind { .. } => {}
+        Node::ArrayKind(_) => {}
+        Node::VariableKind(_) => {}
+        Node::EmptyArgKind => {}
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/expressions/parser/walk.rs.html b/src/ironcalc_base/expressions/parser/walk.rs.html new file mode 100644 index 0000000..e10dc62 --- /dev/null +++ b/src/ironcalc_base/expressions/parser/walk.rs.html @@ -0,0 +1,553 @@ +walk.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+
use super::{move_formula::ref_is_in_area, Node};
+
+use crate::expressions::types::{Area, CellReferenceIndex};
+
+pub(crate) fn forward_references(
+    node: &mut Node,
+    context: &CellReferenceIndex,
+    source_area: &Area,
+    target_sheet: u32,
+    target_sheet_name: &str,
+    target_row: i32,
+    target_column: i32,
+) {
+    match node {
+        Node::ReferenceKind {
+            sheet_name,
+            sheet_index: reference_sheet,
+            absolute_row,
+            absolute_column,
+            row: reference_row,
+            column: reference_column,
+        } => {
+            let reference_row_absolute = if *absolute_row {
+                *reference_row
+            } else {
+                *reference_row + context.row
+            };
+            let reference_column_absolute = if *absolute_column {
+                *reference_column
+            } else {
+                *reference_column + context.column
+            };
+            if ref_is_in_area(
+                *reference_sheet,
+                reference_row_absolute,
+                reference_column_absolute,
+                source_area,
+            ) {
+                if *reference_sheet != target_sheet {
+                    *sheet_name = Some(target_sheet_name.to_string());
+                    *reference_sheet = target_sheet;
+                }
+                *reference_row = target_row + *reference_row - source_area.row;
+                *reference_column = target_column + *reference_column - source_area.column;
+            }
+        }
+        Node::RangeKind {
+            sheet_name,
+            sheet_index,
+            absolute_row1,
+            absolute_column1,
+            row1,
+            column1,
+            absolute_row2,
+            absolute_column2,
+            row2,
+            column2,
+        } => {
+            let reference_row1 = if *absolute_row1 {
+                *row1
+            } else {
+                *row1 + context.row
+            };
+            let reference_column1 = if *absolute_column1 {
+                *column1
+            } else {
+                *column1 + context.column
+            };
+
+            let reference_row2 = if *absolute_row2 {
+                *row2
+            } else {
+                *row2 + context.row
+            };
+            let reference_column2 = if *absolute_column2 {
+                *column2
+            } else {
+                *column2 + context.column
+            };
+            if ref_is_in_area(*sheet_index, reference_row1, reference_column1, source_area)
+                && ref_is_in_area(*sheet_index, reference_row2, reference_column2, source_area)
+            {
+                if *sheet_index != target_sheet {
+                    *sheet_index = target_sheet;
+                    *sheet_name = Some(target_sheet_name.to_string());
+                }
+                *row1 = target_row + *row1 - source_area.row;
+                *column1 = target_column + *column1 - source_area.column;
+                *row2 = target_row + *row2 - source_area.row;
+                *column2 = target_column + *column2 - source_area.column;
+            }
+        }
+        // Recurse
+        Node::OpRangeKind { left, right } => {
+            forward_references(
+                left,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+            forward_references(
+                right,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+        }
+        Node::OpConcatenateKind { left, right } => {
+            forward_references(
+                left,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+            forward_references(
+                right,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+        }
+        Node::OpSumKind {
+            kind: _,
+            left,
+            right,
+        } => {
+            forward_references(
+                left,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+            forward_references(
+                right,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+        }
+        Node::OpProductKind {
+            kind: _,
+            left,
+            right,
+        } => {
+            forward_references(
+                left,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+            forward_references(
+                right,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+        }
+        Node::OpPowerKind { left, right } => {
+            forward_references(
+                left,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+            forward_references(
+                right,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+        }
+        Node::FunctionKind { kind: _, args } => {
+            for arg in args {
+                forward_references(
+                    arg,
+                    context,
+                    source_area,
+                    target_sheet,
+                    target_sheet_name,
+                    target_row,
+                    target_column,
+                );
+            }
+        }
+        Node::InvalidFunctionKind { name: _, args } => {
+            for arg in args {
+                forward_references(
+                    arg,
+                    context,
+                    source_area,
+                    target_sheet,
+                    target_sheet_name,
+                    target_row,
+                    target_column,
+                );
+            }
+        }
+        Node::CompareKind {
+            kind: _,
+            left,
+            right,
+        } => {
+            forward_references(
+                left,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+            forward_references(
+                right,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+        }
+        Node::UnaryKind { kind: _, right } => {
+            forward_references(
+                right,
+                context,
+                source_area,
+                target_sheet,
+                target_sheet_name,
+                target_row,
+                target_column,
+            );
+        }
+        // TODO: Not implemented
+        Node::ArrayKind(_) => {}
+        // Do nothing. Note: we could do a blanket _ => {}
+        Node::VariableKind(_) => {}
+        Node::ErrorKind(_) => {}
+        Node::ParseErrorKind { .. } => {}
+        Node::EmptyArgKind => {}
+        Node::BooleanKind(_) => {}
+        Node::NumberKind(_) => {}
+        Node::StringKind(_) => {}
+        Node::WrongReferenceKind { .. } => {}
+        Node::WrongRangeKind { .. } => {}
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/expressions/token.rs.html b/src/ironcalc_base/expressions/token.rs.html new file mode 100644 index 0000000..deefdc5 --- /dev/null +++ b/src/ironcalc_base/expressions/token.rs.html @@ -0,0 +1,777 @@ +token.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+
use std::fmt;
+
+use serde_repr::{Deserialize_repr, Serialize_repr};
+
+use crate::language::Language;
+
+use super::{lexer::LexerError, types::ParsedReference};
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum OpCompare {
+    LessThan,
+    GreaterThan,
+    Equal,
+    LessOrEqualThan,
+    GreaterOrEqualThan,
+    NonEqual,
+}
+
+impl fmt::Display for OpCompare {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            OpCompare::LessThan => write!(fmt, "<"),
+            OpCompare::GreaterThan => write!(fmt, ">"),
+            OpCompare::Equal => write!(fmt, "="),
+            OpCompare::LessOrEqualThan => write!(fmt, "<="),
+            OpCompare::GreaterOrEqualThan => write!(fmt, ">="),
+            OpCompare::NonEqual => write!(fmt, "<>"),
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum OpUnary {
+    Minus,
+    Percentage,
+}
+
+impl fmt::Display for OpUnary {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            OpUnary::Minus => write!(fmt, "-"),
+            OpUnary::Percentage => write!(fmt, "%"),
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum OpSum {
+    Add,
+    Minus,
+}
+
+impl fmt::Display for OpSum {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            OpSum::Add => write!(fmt, "+"),
+            OpSum::Minus => write!(fmt, "-"),
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum OpProduct {
+    Times,
+    Divide,
+}
+
+impl fmt::Display for OpProduct {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            OpProduct::Times => write!(fmt, "*"),
+            OpProduct::Divide => write!(fmt, "/"),
+        }
+    }
+}
+
+/// List of `errors`
+/// Note that "#ERROR!" and "#N/IMPL!" are not part of the xlsx standard
+///  * "#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
+/// Note that they are serialized/deserialized by index
+#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Eq, Clone)]
+#[repr(u8)]
+pub enum Error {
+    REF,
+    NAME,
+    VALUE,
+    DIV,
+    NA,
+    NUM,
+    ERROR,
+    NIMPL,
+    SPILL,
+    CALC,
+    CIRC,
+    NULL,
+}
+
+impl fmt::Display for Error {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Error::NULL => write!(fmt, "#NULL!"),
+            Error::REF => write!(fmt, "#REF!"),
+            Error::NAME => write!(fmt, "#NAME?"),
+            Error::VALUE => write!(fmt, "#VALUE!"),
+            Error::DIV => write!(fmt, "#DIV/0!"),
+            Error::NA => write!(fmt, "#N/A"),
+            Error::NUM => write!(fmt, "#NUM!"),
+            Error::ERROR => write!(fmt, "#ERROR!"),
+            Error::NIMPL => write!(fmt, "#N/IMPL"),
+            Error::SPILL => write!(fmt, "#SPILL!"),
+            Error::CALC => write!(fmt, "#CALC!"),
+            Error::CIRC => write!(fmt, "#CIRC!"),
+        }
+    }
+}
+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::NAME => language.errors.name.to_string(),
+            Error::VALUE => language.errors.value.to_string(),
+            Error::DIV => language.errors.div.to_string(),
+            Error::NA => language.errors.na.to_string(),
+            Error::NUM => language.errors.num.to_string(),
+            Error::ERROR => language.errors.error.to_string(),
+            Error::NIMPL => language.errors.nimpl.to_string(),
+            Error::SPILL => language.errors.spill.to_string(),
+            Error::CALC => language.errors.calc.to_string(),
+            Error::CIRC => language.errors.circ.to_string(),
+        }
+    }
+}
+
+pub fn get_error_by_name(name: &str, language: &Language) -> Option<Error> {
+    let errors = &language.errors;
+    if name == errors.ref_value {
+        return Some(Error::REF);
+    } else if name == errors.name {
+        return Some(Error::NAME);
+    } else if name == errors.value {
+        return Some(Error::VALUE);
+    } else if name == errors.div {
+        return Some(Error::DIV);
+    } else if name == errors.na {
+        return Some(Error::NA);
+    } else if name == errors.num {
+        return Some(Error::NUM);
+    } else if name == errors.error {
+        return Some(Error::ERROR);
+    } else if name == errors.nimpl {
+        return Some(Error::NIMPL);
+    } else if name == errors.spill {
+        return Some(Error::SPILL);
+    } else if name == errors.calc {
+        return Some(Error::CALC);
+    } else if name == errors.circ {
+        return Some(Error::CIRC);
+    } else if name == errors.null {
+        return Some(Error::NULL);
+    }
+    None
+}
+
+pub fn get_error_by_english_name(name: &str) -> Option<Error> {
+    if name == "#REF!" {
+        return Some(Error::REF);
+    } else if name == "#NAME?" {
+        return Some(Error::NAME);
+    } else if name == "#VALUE!" {
+        return Some(Error::VALUE);
+    } else if name == "#DIV/0!" {
+        return Some(Error::DIV);
+    } else if name == "#N/A" {
+        return Some(Error::NA);
+    } else if name == "#NUM!" {
+        return Some(Error::NUM);
+    } else if name == "#ERROR!" {
+        return Some(Error::ERROR);
+    } else if name == "#N/IMPL!" {
+        return Some(Error::NIMPL);
+    } else if name == "#SPILL!" {
+        return Some(Error::SPILL);
+    } else if name == "#CALC!" {
+        return Some(Error::CALC);
+    } else if name == "#CIRC!" {
+        return Some(Error::CIRC);
+    } else if name == "#NULL!" {
+        return Some(Error::NULL);
+    }
+    None
+}
+
+pub fn is_english_error_string(name: &str) -> bool {
+    let names = [
+        "#REF!", "#NAME?", "#VALUE!", "#DIV/0!", "#N/A", "#NUM!", "#ERROR!", "#N/IMPL!", "#SPILL!",
+        "#CALC!", "#CIRC!", "#NULL!",
+    ];
+    names.iter().any(|e| *e == name)
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum TableSpecifier {
+    All,
+    Data,
+    Headers,
+    ThisRow,
+    Totals,
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum TableReference {
+    ColumnReference(String),
+    RangeReference((String, String)),
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum TokenType {
+    Illegal(LexerError),
+    EOF,
+    Ident(String),      // abc123
+    String(String),     // "A season"
+    Number(f64),        // 123.4
+    Boolean(bool),      // TRUE | FALSE
+    Error(Error),       // #VALUE!
+    Compare(OpCompare), // <,>, ...
+    Addition(OpSum),    // +,-
+    Product(OpProduct), // *,/
+    Power,              // ^
+    LeftParenthesis,    // (
+    RightParenthesis,   // )
+    Colon,              // :
+    Semicolon,          // ;
+    LeftBracket,        // [
+    RightBracket,       // ]
+    LeftBrace,          // {
+    RightBrace,         // }
+    Comma,              // ,
+    Bang,               // !
+    Percent,            // %
+    And,                // &
+    Reference {
+        sheet: Option<String>,
+        row: i32,
+        column: i32,
+        absolute_column: bool,
+        absolute_row: bool,
+    },
+    Range {
+        sheet: Option<String>,
+        left: ParsedReference,
+        right: ParsedReference,
+    },
+    StructuredReference {
+        table_name: String,
+        specifier: Option<TableSpecifier>,
+        table_reference: Option<TableReference>,
+    },
+}
+
+impl fmt::Display for TokenType {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        use self::TokenType::*;
+        match self {
+            Illegal(_) => write!(fmt, "Illegal"),
+            EOF => write!(fmt, ""),
+            Ident(value) => write!(fmt, "{}", value),
+            String(value) => write!(fmt, "\"{}\"", value),
+            Number(value) => write!(fmt, "{}", value),
+            Boolean(value) => write!(fmt, "{}", value),
+            Error(value) => write!(fmt, "{}", value),
+            Compare(value) => write!(fmt, "{}", value),
+            Addition(value) => write!(fmt, "{}", value),
+            Product(value) => write!(fmt, "{}", value),
+            Power => write!(fmt, "^"),
+            LeftParenthesis => write!(fmt, "("),
+            RightParenthesis => write!(fmt, ")"),
+            Colon => write!(fmt, ":"),
+            Semicolon => write!(fmt, ";"),
+            LeftBracket => write!(fmt, "["),
+            RightBracket => write!(fmt, "]"),
+            LeftBrace => write!(fmt, "{{"),
+            RightBrace => write!(fmt, "}}"),
+            Comma => write!(fmt, ","),
+            Bang => write!(fmt, "!"),
+            Percent => write!(fmt, "%"),
+            And => write!(fmt, "&"),
+            Reference {
+                sheet,
+                row,
+                column,
+                absolute_column,
+                absolute_row,
+            } => {
+                let row_data = if *absolute_row {
+                    format!("{}", row)
+                } else {
+                    format!("${}", row)
+                };
+                let column_data = if *absolute_column {
+                    format!("{}", column)
+                } else {
+                    format!("${}", column)
+                };
+                match sheet {
+                    Some(name) => write!(fmt, "{}!{}{}", name, column_data, row_data),
+                    None => write!(fmt, "{}{}", column, row),
+                }
+            }
+            Range { sheet, left, right } => {
+                let row_left_data = if left.absolute_row {
+                    format!("{}", left.row)
+                } else {
+                    format!("${}", left.row)
+                };
+                let column_left_data = if left.absolute_column {
+                    format!("{}", left.column)
+                } else {
+                    format!("${}", left.column)
+                };
+
+                let row_right_data = if right.absolute_row {
+                    format!("{}", right.row)
+                } else {
+                    format!("${}", right.row)
+                };
+                let column_right_data = if right.absolute_column {
+                    format!("{}", right.column)
+                } else {
+                    format!("${}", right.column)
+                };
+                match sheet {
+                    Some(name) => write!(
+                        fmt,
+                        "{}!{}{}:{}{}",
+                        name, column_left_data, row_left_data, column_right_data, row_right_data
+                    ),
+                    None => write!(
+                        fmt,
+                        "{}{}:{}{}",
+                        left.column, left.row, right.column, right.row
+                    ),
+                }
+            }
+            StructuredReference {
+                table_name: _,
+                specifier: _,
+                table_reference: _,
+            } => {
+                // This should never happen
+                write!(fmt, "-----ERROR-----")
+            }
+        }
+    }
+}
+
+pub fn index(token: &TokenType) -> u32 {
+    use self::TokenType::*;
+    match token {
+        Illegal(..) => 1,
+        EOF => 2,
+        Ident(..) => 3,
+        String(..) => 4,
+        Number(..) => 6,
+        Boolean(..) => 7,
+        Error(..) => 8,
+        Addition(..) => 9,
+        Product(..) => 10,
+        Power => 14,
+        LeftParenthesis => 15,
+        RightParenthesis => 16,
+        Colon => 17,
+        Semicolon => 18,
+        LeftBracket => 19,
+        RightBracket => 20,
+        LeftBrace => 21,
+        RightBrace => 22,
+        Comma => 23,
+        Bang => 24,
+        Percent => 30,
+        And => 31,
+        Reference { .. } => 34,
+        Range { .. } => 35,
+        Compare(..) => 37,
+        StructuredReference { .. } => 40,
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/expressions/types.rs.html b/src/ironcalc_base/expressions/types.rs.html new file mode 100644 index 0000000..a7b5343 --- /dev/null +++ b/src/ironcalc_base/expressions/types.rs.html @@ -0,0 +1,103 @@ +types.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+
use serde::{Deserialize, Serialize};
+
+// $A$34
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
+pub struct ParsedReference {
+    pub column: i32,
+    pub row: i32,
+    pub absolute_column: bool,
+    pub absolute_row: bool,
+}
+
+/// If right is None it is just a reference
+/// Column ranges like D:D will have `absolute_row=true` and `left.row=1` and `right.row=LAST_ROW`
+/// Row ranges like 5:5 will have `absolute_column=true` and `left.column=1` and `right.column=LAST_COLUMN`
+pub struct ParsedRange {
+    pub left: ParsedReference,
+    pub right: Option<ParsedReference>,
+}
+
+// FIXME: It does not make sense to have two different structures.
+// We should have a single one CellReferenceNamed or something like that.
+// Sheet1!C3
+pub struct CellReference {
+    pub sheet: String,
+    pub column: String,
+    pub row: String,
+}
+
+// Sheet1!C3 -> CellReferenceRC{Sheet1, 3, 3}
+#[derive(Clone)]
+pub struct CellReferenceRC {
+    pub sheet: String,
+    pub column: i32,
+    pub row: i32,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
+pub struct CellReferenceIndex {
+    pub sheet: u32,
+    pub column: i32,
+    pub row: i32,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct Area {
+    pub sheet: u32,
+    pub row: i32,
+    pub column: i32,
+    pub width: i32,
+    pub height: i32,
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/expressions/utils/mod.rs.html b/src/ironcalc_base/expressions/utils/mod.rs.html new file mode 100644 index 0000000..a4c41c4 --- /dev/null +++ b/src/ironcalc_base/expressions/utils/mod.rs.html @@ -0,0 +1,563 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+
use super::types::*;
+use crate::constants::{LAST_COLUMN, LAST_ROW};
+
+#[cfg(test)]
+mod test;
+
+/// Converts column letter identifier to number.
+pub fn column_to_number(column: &str) -> Result<i32, String> {
+    if column.is_empty() {
+        return Err("Column identifier cannot be empty.".to_string());
+    }
+
+    if !column.is_ascii() {
+        return Err("Column identifier must be ASCII.".to_string());
+    }
+
+    let mut column_number = 0;
+    for character in column.chars() {
+        if !character.is_ascii_uppercase() {
+            return Err("Column identifier can use only A-Z characters".to_string());
+        }
+        column_number = column_number * 26 + ((character as i32) - 64);
+    }
+
+    match is_valid_column_number(column_number) {
+        true => Ok(column_number),
+        false => Err("Column is not valid.".to_string()),
+    }
+}
+
+/// If input number is outside valid range `None` is returned.
+pub fn number_to_column(mut i: i32) -> Option<String> {
+    if !is_valid_column_number(i) {
+        return None;
+    }
+    let mut column = "".to_string();
+    while i > 0 {
+        let r = ((i - 1) % 26) as u8;
+        column.insert(0, (65 + r) as char);
+        i = (i - 1) / 26;
+    }
+    Some(column)
+}
+
+/// Checks if column number is in valid range.
+pub fn is_valid_column_number(column: i32) -> bool {
+    (1..=LAST_COLUMN).contains(&column)
+}
+
+pub fn is_valid_column(column: &str) -> bool {
+    // last column XFD
+    if column.len() > 3 {
+        return false;
+    }
+
+    let column_number = column_to_number(column);
+
+    match column_number {
+        Ok(column_number) => is_valid_column_number(column_number),
+        Err(_) => false,
+    }
+}
+
+pub fn is_valid_row(row: i32) -> bool {
+    (1..=LAST_ROW).contains(&row)
+}
+
+fn is_valid_row_str(row: &str) -> bool {
+    match row.parse::<i32>() {
+        Ok(r) => is_valid_row(r),
+        Err(_r) => false,
+    }
+}
+
+pub fn parse_reference_r1c1(r: &str) -> Option<ParsedReference> {
+    let chars = r.as_bytes();
+    let len = chars.len();
+    let absolute_column;
+    let absolute_row;
+    let mut row = "".to_string();
+    let mut column = "".to_string();
+    if len < 4 {
+        return None;
+    }
+    if chars[0] != b'R' {
+        return None;
+    }
+    let mut i = 1;
+    if chars[i] == b'[' {
+        i += 1;
+        absolute_row = false;
+        if chars[i] == b'-' {
+            i += 1;
+            row.push('-');
+        }
+    } else {
+        absolute_row = true;
+    }
+    while i < len {
+        let ch = chars[i];
+        if ch.is_ascii_digit() {
+            row.push(ch as char);
+        } else {
+            break;
+        }
+        i += 1;
+    }
+    if !absolute_row {
+        if i >= len || chars[i] != b']' {
+            return None;
+        };
+        i += 1;
+    }
+    if i >= len || chars[i] != b'C' {
+        return None;
+    };
+    i += 1;
+    if i < len && chars[i] == b'[' {
+        absolute_column = false;
+        i += 1;
+        if i < len && chars[i] == b'-' {
+            i += 1;
+            column.push('-');
+        }
+    } else {
+        absolute_column = true;
+    }
+    while i < len {
+        let ch = chars[i];
+        if ch.is_ascii_digit() {
+            column.push(ch as char);
+        } else {
+            break;
+        }
+        i += 1;
+    }
+    if !absolute_column {
+        if i >= len || chars[i] != b']' {
+            return None;
+        };
+        i += 1;
+    }
+    if i != len {
+        return None;
+    }
+    Some(ParsedReference {
+        row: row.parse::<i32>().unwrap_or(0),
+        column: column.parse::<i32>().unwrap_or(0),
+        absolute_column,
+        absolute_row,
+    })
+}
+
+pub fn parse_reference_a1(r: &str) -> Option<ParsedReference> {
+    let chars = r.chars();
+    let mut absolute_column = false;
+    let mut absolute_row = false;
+    let mut row = "".to_string();
+    let mut column = "".to_string();
+    let mut state = 1; // 1(colum), 2(row)
+
+    for ch in chars {
+        match ch {
+            'A'..='Z' => {
+                if state == 1 {
+                    column.push(ch);
+                } else {
+                    return None;
+                }
+            }
+            '0'..='9' => {
+                if state == 1 {
+                    state = 2
+                }
+                row.push(ch);
+            }
+            '$' => {
+                if column == *"" {
+                    absolute_column = true;
+                } else if state == 1 {
+                    absolute_row = true;
+                    state = 2;
+                } else {
+                    return None;
+                }
+            }
+            _ => {
+                return None;
+            }
+        }
+    }
+    if !is_valid_column(&column) {
+        return None;
+    }
+    if !is_valid_row_str(&row) {
+        return None;
+    }
+    let row = match row.parse::<i32>() {
+        Ok(r) => r,
+        Err(_) => return None,
+    };
+
+    Some(ParsedReference {
+        row,
+        column: column_to_number(&column).ok()?,
+        absolute_column,
+        absolute_row,
+    })
+}
+
+pub fn is_valid_identifier(name: &str) -> bool {
+    // https://support.microsoft.com/en-us/office/names-in-formulas-fc2935f9-115d-4bef-a370-3aa8bb4c91f1
+    // https://github.com/MartinTrummer/excel-names/
+    // NOTE: We are being much more restrictive than Excel.
+    // In particular we do not support non ascii characters.
+    let upper = name.to_ascii_uppercase();
+    let bytes = upper.as_bytes();
+    let len = bytes.len();
+    if len > 255 || len == 0 {
+        return false;
+    }
+    let first = bytes[0] as char;
+    // The first character of a name must be a letter, an underscore character (_), or a backslash (\).
+    if !(first.is_ascii_alphabetic() || first == '_' || first == '\\') {
+        return false;
+    }
+    // You cannot use the uppercase and lowercase characters "C", "c", "R", or "r" as a defined name
+    if len == 1 && (first == 'R' || first == 'C') {
+        return false;
+    }
+    if upper == *"TRUE" || upper == *"FALSE" {
+        return false;
+    }
+    if parse_reference_a1(name).is_some() {
+        return false;
+    }
+    if parse_reference_r1c1(name).is_some() {
+        return false;
+    }
+    let mut i = 1;
+    while i < len {
+        let ch = bytes[i] as char;
+        match ch {
+            'a'..='z' => {}
+            'A'..='Z' => {}
+            '0'..='9' => {}
+            '_' => {}
+            '.' => {}
+            _ => {
+                return false;
+            }
+        }
+        i += 1;
+    }
+
+    true
+}
+
+fn name_needs_quoting(name: &str) -> bool {
+    let chars = name.chars();
+    // it contains any of these characters: ()'$,;-+{} or space
+    for char in chars {
+        if [' ', '(', ')', '\'', '$', ',', ';', '-', '+', '{', '}'].contains(&char) {
+            return true;
+        }
+    }
+    // TODO:
+    // cell reference in A1 notation, e.g. B1048576 is quoted, B1048577 is not
+    // cell reference in R1C1 notation, e.g. RC, RC2, R5C, R-4C, RC-8, R, C
+    // integers
+    false
+}
+
+/// Quotes a string sheet name if it needs to
+/// NOTE: Invalid characters in a sheet name \, /, *, \[, \], :, ?
+pub fn quote_name(name: &str) -> String {
+    if name_needs_quoting(name) {
+        return format!("'{}'", name.replace('\'', "''"));
+    };
+    name.to_string()
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/formatter/dates.rs.html b/src/ironcalc_base/formatter/dates.rs.html new file mode 100644 index 0000000..e052a5c --- /dev/null +++ b/src/ironcalc_base/formatter/dates.rs.html @@ -0,0 +1,35 @@ +dates.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
use chrono::Datelike;
+use chrono::Duration;
+use chrono::NaiveDate;
+
+use crate::constants::EXCEL_DATE_BASE;
+
+pub fn from_excel_date(days: i64) -> NaiveDate {
+    let dt = NaiveDate::from_ymd_opt(1900, 1, 1).expect("problem with chrono::NaiveDate");
+    dt + Duration::days(days - 2)
+}
+
+pub fn date_to_serial_number(day: u32, month: u32, year: i32) -> Result<i32, String> {
+    match NaiveDate::from_ymd_opt(year, month, day) {
+        Some(native_date) => Ok(native_date.num_days_from_ce() - EXCEL_DATE_BASE),
+        None => Err("Out of range parameters for date".to_string()),
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/formatter/format.rs.html b/src/ironcalc_base/formatter/format.rs.html new file mode 100644 index 0000000..646ce71 --- /dev/null +++ b/src/ironcalc_base/formatter/format.rs.html @@ -0,0 +1,1527 @@ +format.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+
use chrono::Datelike;
+
+use crate::{locale::Locale, number_format::to_precision};
+
+use super::{
+    dates::{date_to_serial_number, from_excel_date},
+    parser::{ParsePart, Parser, TextToken},
+};
+
+pub struct Formatted {
+    pub color: Option<i32>,
+    pub text: String,
+    pub error: Option<String>,
+}
+
+/// Returns the vector of chars of the fractional part of a *positive* number:
+/// 3.1415926 ==> ['1', '4', '1', '5', '9', '2', '6']
+fn get_fract_part(value: f64, precision: i32) -> Vec<char> {
+    let b = format!("{:.1$}", value.fract(), precision as usize)
+        .chars()
+        .collect::<Vec<char>>();
+    let l = b.len() - 1;
+    let mut last_non_zero = b.len() - 1;
+    for i in 0..l {
+        if b[l - i] != '0' {
+            last_non_zero = l - i + 1;
+            break;
+        }
+    }
+    if last_non_zero < 2 {
+        return vec![];
+    }
+    b[2..last_non_zero].to_vec()
+}
+
+/// Return true if we need to add a separator in position digit_index
+/// It normally happens if if digit_index -1 is 3, 6, 9,... digit_index ≡ 1 mod 3
+fn use_group_separator(use_thousands: bool, digit_index: i32, group_sizes: &str) -> bool {
+    if use_thousands {
+        if group_sizes == "#,##0.###" {
+            if digit_index > 1 && (digit_index - 1) % 3 == 0 {
+                return true;
+            }
+        } else if group_sizes == "#,##,##0.###"
+            && (digit_index == 3 || (digit_index > 3 && digit_index % 2 == 0))
+        {
+            return true;
+        }
+    }
+    false
+}
+
+pub fn format_number(value_original: f64, format: &str, locale: &Locale) -> Formatted {
+    let mut parser = Parser::new(format);
+    parser.parse();
+    let parts = parser.parts;
+    // There are four parts:
+    // 1) Positive numbers
+    // 2) Negative numbers
+    // 3) Zero
+    // 4) Text
+    // If you specify only one section of format code, the code in that section is used for all numbers.
+    // If you specify two sections of format code, the first section of code is used
+    // for positive numbers and zeros, and the second section of code is used for negative numbers.
+    // When you skip code sections in your number format,
+    // you must include a semicolon for each of the missing sections of code.
+    // You can use the ampersand (&) text operator to join, or concatenate, two values.
+    let mut value = value_original;
+    let part;
+    match parts.len() {
+        1 => {
+            part = &parts[0];
+        }
+        2 => {
+            if value >= 0.0 {
+                part = &parts[0]
+            } else {
+                value = -value;
+                part = &parts[1];
+            }
+        }
+        3 => {
+            if value > 0.0 {
+                part = &parts[0]
+            } else if value < 0.0 {
+                value = -value;
+                part = &parts[1];
+            } else {
+                value = 0.0;
+                part = &parts[2];
+            }
+        }
+        4 => {
+            if value > 0.0 {
+                part = &parts[0]
+            } else if value < 0.0 {
+                value = -value;
+                part = &parts[1];
+            } else {
+                value = 0.0;
+                part = &parts[2];
+            }
+        }
+        _ => {
+            return Formatted {
+                text: "#VALUE!".to_owned(),
+                color: None,
+                error: Some("Too many parts".to_owned()),
+            };
+        }
+    }
+    match part {
+        ParsePart::Error(..) => Formatted {
+            text: "#VALUE!".to_owned(),
+            color: None,
+            error: Some("Problem parsing format string".to_owned()),
+        },
+        ParsePart::General(..) => {
+            // FIXME: This is "General formatting"
+            // We should have different codepaths for general formatting and errors
+            let value_abs = value.abs();
+            if (1.0e-8..1.0e+11).contains(&value_abs) {
+                let mut text = format!("{:.9}", value);
+                text = text.trim_end_matches('0').trim_end_matches('.').to_string();
+                Formatted {
+                    text,
+                    color: None,
+                    error: None,
+                }
+            } else {
+                if value_abs == 0.0 {
+                    return Formatted {
+                        text: "0".to_string(),
+                        color: None,
+                        error: None,
+                    };
+                }
+                let exponent = value_abs.log10().floor();
+                value /= 10.0_f64.powf(exponent);
+                let sign = if exponent < 0.0 { '-' } else { '+' };
+                let s = format!("{:.5}", value);
+                Formatted {
+                    text: format!(
+                        "{}E{}{:02}",
+                        s.trim_end_matches('0').trim_end_matches('.'),
+                        sign,
+                        exponent.abs()
+                    ),
+                    color: None,
+                    error: None,
+                }
+            }
+        }
+        ParsePart::Date(p) => {
+            let tokens = &p.tokens;
+            let mut text = "".to_string();
+            if !(1.0..=2_958_465.0).contains(&value) {
+                // 2_958_465 is 31 December 9999
+                return Formatted {
+                    text: "#VALUE!".to_owned(),
+                    color: None,
+                    error: Some("Date negative or too long".to_owned()),
+                };
+            }
+            let date = from_excel_date(value as i64);
+            for token in tokens {
+                match token {
+                    TextToken::Literal(c) => {
+                        text = format!("{}{}", text, c);
+                    }
+                    TextToken::Text(t) => {
+                        text = format!("{}{}", text, t);
+                    }
+                    TextToken::Ghost(_) => {
+                        // we just leave a whitespace
+                        // This is what the TEXT function does
+                        text = format!("{} ", text);
+                    }
+                    TextToken::Spacer(_) => {
+                        // we just leave a whitespace
+                        // This is what the TEXT function does
+                        text = format!("{} ", text);
+                    }
+                    TextToken::Raw => {
+                        text = format!("{}{}", text, value);
+                    }
+                    TextToken::Digit(_) => {}
+                    TextToken::Period => {}
+                    TextToken::Day => {
+                        let day = date.day() as usize;
+                        text = format!("{}{}", text, day);
+                    }
+                    TextToken::DayPadded => {
+                        let day = date.day() as usize;
+                        text = format!("{}{:02}", text, day);
+                    }
+                    TextToken::DayNameShort => {
+                        let mut day = date.weekday().number_from_monday() as usize;
+                        if day == 7 {
+                            day = 0;
+                        }
+                        text = format!("{}{}", text, &locale.dates.day_names_short[day]);
+                    }
+                    TextToken::DayName => {
+                        let mut day = date.weekday().number_from_monday() as usize;
+                        if day == 7 {
+                            day = 0;
+                        }
+                        text = format!("{}{}", text, &locale.dates.day_names[day]);
+                    }
+                    TextToken::Month => {
+                        let month = date.month() as usize;
+                        text = format!("{}{}", text, month);
+                    }
+                    TextToken::MonthPadded => {
+                        let month = date.month() as usize;
+                        text = format!("{}{:02}", text, month);
+                    }
+                    TextToken::MonthNameShort => {
+                        let month = date.month() as usize;
+                        text = format!("{}{}", text, &locale.dates.months_short[month - 1]);
+                    }
+                    TextToken::MonthName => {
+                        let month = date.month() as usize;
+                        text = format!("{}{}", text, &locale.dates.months[month - 1]);
+                    }
+                    TextToken::MonthLetter => {
+                        let month = date.month() as usize;
+                        let months_letter = &locale.dates.months_letter[month - 1];
+                        text = format!("{}{}", text, months_letter);
+                    }
+                    TextToken::YearShort => {
+                        text = format!("{}{}", text, date.format("%y"));
+                    }
+                    TextToken::Year => {
+                        text = format!("{}{}", text, date.year());
+                    }
+                }
+            }
+            Formatted {
+                text,
+                color: p.color,
+                error: None,
+            }
+        }
+        ParsePart::Number(p) => {
+            let mut text = "".to_string();
+            let tokens = &p.tokens;
+            value = value * 100.0_f64.powi(p.percent) / (1000.0_f64.powi(p.comma));
+            // p.precision is the number of significant digits _after_ the decimal point
+            value = to_precision(
+                value,
+                (p.precision as usize) + format!("{}", value.abs().floor()).len(),
+            );
+            let mut value_abs = value.abs();
+            let mut exponent_part: Vec<char> = vec![];
+            let mut exponent_is_negative = value_abs < 10.0;
+            if p.is_scientific {
+                if value_abs == 0.0 {
+                    exponent_part = vec!['0'];
+                    exponent_is_negative = false;
+                } else {
+                    // TODO: Implement engineering formatting.
+                    let exponent = value_abs.log10().floor();
+                    exponent_part = format!("{}", exponent.abs()).chars().collect();
+                    value /= 10.0_f64.powf(exponent);
+                    value = to_precision(value, 15);
+                    value_abs = value.abs();
+                }
+            }
+            let l_exp = exponent_part.len() as i32;
+            let mut int_part: Vec<char> = format!("{}", value_abs.floor()).chars().collect();
+            if value_abs as i64 == 0 {
+                int_part = vec![];
+            }
+            let fract_part = get_fract_part(value_abs, p.precision);
+            // ln is the number of digits of the integer part of the value
+            let ln = int_part.len() as i32;
+            // digit count is the number of digit tokens ('0', '?' and '#') to the left of the decimal point
+            let digit_count = p.digit_count;
+            // digit_index points to the digit index in value that we have already formatted
+            let mut digit_index = 0;
+
+            let symbols = &locale.numbers.symbols;
+            let group_sizes = locale.numbers.decimal_formats.standard.to_owned();
+            let group_separator = symbols.group.to_owned();
+            let decimal_separator = symbols.decimal.to_owned();
+            // There probably are better ways to check if a number at a given precision is negative :/
+            let is_negative = value < -(10.0_f64.powf(-(p.precision as f64)));
+
+            for token in tokens {
+                match token {
+                    TextToken::Literal(c) => {
+                        text = format!("{}{}", text, c);
+                    }
+                    TextToken::Text(t) => {
+                        text = format!("{}{}", text, t);
+                    }
+                    TextToken::Ghost(_) => {
+                        // we just leave a whitespace
+                        // This is what the TEXT function does
+                        text = format!("{} ", text);
+                    }
+                    TextToken::Spacer(_) => {
+                        // we just leave a whitespace
+                        // This is what the TEXT function does
+                        text = format!("{} ", text);
+                    }
+                    TextToken::Raw => {
+                        text = format!("{}{}", text, value);
+                    }
+                    TextToken::Period => {
+                        text = format!("{}{}", text, decimal_separator);
+                    }
+                    TextToken::Digit(digit) => {
+                        if digit.number == 'i' {
+                            // 1. Integer part
+                            let index = digit.index;
+                            let number_index = ln - digit_count + index;
+                            if index == 0 && is_negative {
+                                text = format!("-{}", text);
+                            }
+                            if ln <= digit_count {
+                                // The number of digits is less or equal than the number of digit tokens
+                                // i.e. the value is 123 and the format_code is ##### (ln = 3 and digit_count = 5)
+                                if !(number_index < 0 && digit.kind == '#') {
+                                    let c = if number_index < 0 {
+                                        if digit.kind == '0' {
+                                            '0'
+                                        } else {
+                                            // digit.kind = '?'
+                                            ' '
+                                        }
+                                    } else {
+                                        int_part[number_index as usize]
+                                    };
+                                    let sep = if use_group_separator(
+                                        p.use_thousands,
+                                        ln - digit_index,
+                                        &group_sizes,
+                                    ) {
+                                        &group_separator
+                                    } else {
+                                        ""
+                                    };
+                                    text = format!("{}{}{}", text, c, sep);
+                                }
+                                digit_index += 1;
+                            } else {
+                                // The number is larger than the formatting code 12345 and 0##
+                                // We just hit the first formatting digit (0 in the example above) so we write as many digits as we can (123 in the example)
+                                for i in digit_index..number_index + 1 {
+                                    let sep = if use_group_separator(
+                                        p.use_thousands,
+                                        ln - i,
+                                        &group_sizes,
+                                    ) {
+                                        &group_separator
+                                    } else {
+                                        ""
+                                    };
+                                    text = format!("{}{}{}", text, int_part[i as usize], sep);
+                                }
+                                digit_index = number_index + 1;
+                            }
+                        } else if digit.number == 'd' {
+                            // 2. After the decimal point
+                            let index = digit.index as usize;
+                            if index < fract_part.len() {
+                                text = format!("{}{}", text, fract_part[index]);
+                            } else if digit.kind == '0' {
+                                text = format!("{}0", text);
+                            } else if digit.kind == '?' {
+                                text = format!("{} ", text);
+                            }
+                        } else if digit.number == 'e' {
+                            // 3. Exponent part
+                            let index = digit.index;
+                            if index == 0 {
+                                if exponent_is_negative {
+                                    text = format!("{}E-", text);
+                                } else {
+                                    text = format!("{}E+", text);
+                                }
+                            }
+                            let number_index = l_exp - (p.exponent_digit_count - index);
+                            if l_exp <= p.exponent_digit_count {
+                                if !(number_index < 0 && digit.kind == '#') {
+                                    let c = if number_index < 0 {
+                                        if digit.kind == '?' {
+                                            ' '
+                                        } else {
+                                            '0'
+                                        }
+                                    } else {
+                                        exponent_part[number_index as usize]
+                                    };
+
+                                    text = format!("{}{}", text, c);
+                                }
+                            } else {
+                                for i in 0..number_index + 1 {
+                                    text = format!("{}{}", text, exponent_part[i as usize]);
+                                }
+                                digit_index += number_index + 1;
+                            }
+                        }
+                    }
+                    // Date tokens should not be present
+                    TextToken::Day => {}
+                    TextToken::DayPadded => {}
+                    TextToken::DayNameShort => {}
+                    TextToken::DayName => {}
+                    TextToken::Month => {}
+                    TextToken::MonthPadded => {}
+                    TextToken::MonthNameShort => {}
+                    TextToken::MonthName => {}
+                    TextToken::MonthLetter => {}
+                    TextToken::YearShort => {}
+                    TextToken::Year => {}
+                }
+            }
+            Formatted {
+                text,
+                color: p.color,
+                error: None,
+            }
+        }
+    }
+}
+
+fn parse_day(day_str: &str) -> Result<(u32, String), String> {
+    let bytes = day_str.bytes();
+    let bytes_len = bytes.len();
+    if bytes_len <= 2 {
+        match day_str.parse::<u32>() {
+            Ok(y) => {
+                if bytes_len == 2 {
+                    return Ok((y, "dd".to_string()));
+                } else {
+                    return Ok((y, "d".to_string()));
+                }
+            }
+            Err(_) => return Err("Not a valid year".to_string()),
+        }
+    }
+    Err("Not a valid day".to_string())
+}
+
+fn parse_month(month_str: &str) -> Result<(u32, String), String> {
+    let bytes = month_str.bytes();
+    let bytes_len = bytes.len();
+    if bytes_len <= 2 {
+        match month_str.parse::<u32>() {
+            Ok(y) => {
+                if bytes_len == 2 {
+                    return Ok((y, "mm".to_string()));
+                } else {
+                    return Ok((y, "m".to_string()));
+                }
+            }
+            Err(_) => return Err("Not a valid year".to_string()),
+        }
+    }
+    let month_names_short = [
+        "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+    ];
+    let month_names_long = [
+        "January",
+        "February",
+        "March",
+        "April",
+        "May",
+        "June",
+        "July",
+        "August",
+        "September",
+        "October",
+        "November",
+        "December",
+    ];
+    if let Some(m) = month_names_short.iter().position(|&r| r == month_str) {
+        return Ok((m as u32 + 1, "mmm".to_string()));
+    }
+    if let Some(m) = month_names_long.iter().position(|&r| r == month_str) {
+        return Ok((m as u32 + 1, "mmmm".to_string()));
+    }
+    Err("Not a valid day".to_string())
+}
+
+fn parse_year(year_str: &str) -> Result<(i32, String), String> {
+    // year is either 2 digits or 4 digits
+    // 23 -> 2023
+    // 75 -> 1975
+    // 30 is the split number (yeah, that's not going to be a problem any time soon)
+    // 30 => 1930
+    // 29 => 2029
+    let bytes = year_str.bytes();
+    let bytes_len = bytes.len();
+    if bytes_len != 2 && bytes_len != 4 {
+        return Err("Not a valid year".to_string());
+    }
+    match year_str.parse::<i32>() {
+        Ok(y) => {
+            if y < 30 {
+                Ok((2000 + y, "yy".to_string()))
+            } else if y < 100 {
+                Ok((1900 + y, "yy".to_string()))
+            } else {
+                Ok((y, "yyyy".to_string()))
+            }
+        }
+        Err(_) => Err("Not a valid year".to_string()),
+    }
+}
+
+// Check if it is a date. Other spreadsheet engines support a wide variety of dates formats
+// Here we support a small subset of them.
+//
+// The grammar is:
+//
+// date -> long_date | short_date | iso-date
+// short_date -> month separator year
+// long_date -> day separator month separator year
+// iso_date -> long_year separator number_month separator number_day
+// separator -> "/" | "-"
+// day -> number | padded number
+// month -> number_month | name_month
+// number_month -> number | padded number |
+// name_month -> short name | full name
+// year -> short_year | long year
+//
+// NOTE 1: The separator has to be the same
+// NOTE 2: In some engines "2/3" is implemented ad "2/March of the present year"
+// NOTE 3: I did not implement the "short date"
+fn parse_date(value: &str) -> Result<(i32, String), String> {
+    let separator = if value.contains('/') {
+        '/'
+    } else if value.contains('-') {
+        '-'
+    } else {
+        return Err("Not a valid date".to_string());
+    };
+
+    let parts: Vec<&str> = value.split(separator).collect();
+    let mut is_iso_date = false;
+    let (day_str, month_str, year_str) = if parts.len() == 3 {
+        if parts[0].len() == 4 {
+            // ISO date  yyyy-mm-dd
+            if !parts[1].chars().all(char::is_numeric) {
+                return Err("Not a valid date".to_string());
+            }
+            if !parts[2].chars().all(char::is_numeric) {
+                return Err("Not a valid date".to_string());
+            }
+            is_iso_date = true;
+            (parts[2], parts[1], parts[0])
+        } else {
+            (parts[0], parts[1], parts[2])
+        }
+    } else {
+        return Err("Not a valid date".to_string());
+    };
+    let (day, day_format) = parse_day(day_str)?;
+    let (month, month_format) = parse_month(month_str)?;
+    let (year, year_format) = parse_year(year_str)?;
+    let serial_number = match date_to_serial_number(day, month, year) {
+        Ok(n) => n,
+        Err(_) => return Err("Not a valid date".to_string()),
+    };
+    if is_iso_date {
+        Ok((
+            serial_number,
+            format!("yyyy{separator}{month_format}{separator}{day_format}"),
+        ))
+    } else {
+        Ok((
+            serial_number,
+            format!("{day_format}{separator}{month_format}{separator}{year_format}"),
+        ))
+    }
+}
+
+/// Parses a formatted number, returning the numeric value together with the format
+/// Uses heuristics to guess the format string
+/// "$ 123,345.678" => (123345.678, "$#,##0.00")
+/// "30.34%" => (0.3034, "0.00%")
+/// 100€ => (100, "100€")
+pub(crate) fn parse_formatted_number(
+    value: &str,
+    currencies: &[&str],
+) -> Result<(f64, Option<String>), String> {
+    let value = value.trim();
+    let scientific_format = "0.00E+00";
+
+    // Check if it is a percentage
+    if let Some(p) = value.strip_suffix('%') {
+        let (f, options) = parse_number(p.trim())?;
+        if options.is_scientific {
+            return Ok((f / 100.0, Some(scientific_format.to_string())));
+        }
+        // We ignore the separator
+        if options.decimal_digits > 0 {
+            // Percentage format with decimals
+            return Ok((f / 100.0, Some("#,##0.00%".to_string())));
+        }
+        // Percentage format standard
+        return Ok((f / 100.0, Some("#,##0%".to_string())));
+    }
+
+    // check if it is a currency in currencies
+    for currency in currencies {
+        if let Some(p) = value.strip_prefix(&format!("-{}", currency)) {
+            let (f, options) = parse_number(p.trim())?;
+            if options.is_scientific {
+                return Ok((f, Some(scientific_format.to_string())));
+            }
+            if options.decimal_digits > 0 {
+                return Ok((-f, Some(format!("{currency}#,##0.00"))));
+            }
+            return Ok((-f, Some(format!("{currency}#,##0"))));
+        } else if let Some(p) = value.strip_prefix(currency) {
+            let (f, options) = parse_number(p.trim())?;
+            if options.is_scientific {
+                return Ok((f, Some(scientific_format.to_string())));
+            }
+            if options.decimal_digits > 0 {
+                return Ok((f, Some(format!("{currency}#,##0.00"))));
+            }
+            return Ok((f, Some(format!("{currency}#,##0"))));
+        } else if let Some(p) = value.strip_suffix(currency) {
+            let (f, options) = parse_number(p.trim())?;
+            if options.is_scientific {
+                return Ok((f, Some(scientific_format.to_string())));
+            }
+            if options.decimal_digits > 0 {
+                let currency_format = &format!("#,##0.00{currency}");
+                return Ok((f, Some(currency_format.to_string())));
+            }
+            let currency_format = &format!("#,##0{currency}");
+            return Ok((f, Some(currency_format.to_string())));
+        }
+    }
+
+    if let Ok((serial_number, format)) = parse_date(value) {
+        return Ok((serial_number as f64, Some(format)));
+    }
+
+    // Lastly we check if it is a number
+    let (f, options) = parse_number(value)?;
+    if options.is_scientific {
+        return Ok((f, Some(scientific_format.to_string())));
+    }
+    if options.has_commas {
+        if options.decimal_digits > 0 {
+            // group separator and two decimal points
+            return Ok((f, Some("#,##0.00".to_string())));
+        }
+        // Group separator and no decimal points
+        return Ok((f, Some("#,##0".to_string())));
+    }
+    Ok((f, None))
+}
+
+struct NumberOptions {
+    has_commas: bool,
+    is_scientific: bool,
+    decimal_digits: usize,
+}
+
+// tries to parse 'value' as a number.
+// If it is a number it either uses commas as thousands separator or it does not
+fn parse_number(value: &str) -> Result<(f64, NumberOptions), String> {
+    let mut position = 0;
+    let bytes = value.as_bytes();
+    let len = bytes.len();
+    if len == 0 {
+        return Err("Cannot parse number".to_string());
+    }
+    let mut chars = String::from("");
+    let decimal_separator = b'.';
+    let group_separator = b',';
+    let mut group_separator_index = Vec::new();
+    // get the sign
+    let sign = if bytes[0] == b'-' {
+        position += 1;
+        -1.0
+    } else if bytes[0] == b'+' {
+        position += 1;
+        1.0
+    } else {
+        1.0
+    };
+    // numbers before the decimal point
+    while position < len {
+        let x = bytes[position];
+        if x.is_ascii_digit() {
+            chars.push(x as char);
+        } else if x == group_separator {
+            group_separator_index.push(chars.len());
+        } else {
+            break;
+        }
+        position += 1;
+    }
+    // Check the group separator is in multiples of three
+    for index in &group_separator_index {
+        if (chars.len() - index) % 3 != 0 {
+            return Err("Cannot parse number".to_string());
+        }
+    }
+    let mut decimal_digits = 0;
+    if position < len && bytes[position] == decimal_separator {
+        // numbers after the decimal point
+        chars.push('.');
+        position += 1;
+        let start_position = 0;
+        while position < len {
+            let x = bytes[position];
+            if x.is_ascii_digit() {
+                chars.push(x as char);
+            } else {
+                break;
+            }
+            position += 1;
+        }
+        decimal_digits = position - start_position;
+    }
+    let mut is_scientific = false;
+    if position + 1 < len && (bytes[position] == b'e' || bytes[position] == b'E') {
+        // exponential side
+        is_scientific = true;
+        let x = bytes[position + 1];
+        if x == b'-' || x == b'+' || x.is_ascii_digit() {
+            chars.push('e');
+            chars.push(x as char);
+            position += 2;
+            while position < len {
+                let x = bytes[position];
+                if x.is_ascii_digit() {
+                    chars.push(x as char);
+                } else {
+                    break;
+                }
+                position += 1;
+            }
+        }
+    }
+    if position != len {
+        return Err("Could not parse number".to_string());
+    };
+    match chars.parse::<f64>() {
+        Err(_) => Err("Failed to parse to double".to_string()),
+        Ok(v) => Ok((
+            sign * v,
+            NumberOptions {
+                has_commas: !group_separator_index.is_empty(),
+                is_scientific,
+                decimal_digits,
+            },
+        )),
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/formatter/lexer.rs.html b/src/ironcalc_base/formatter/lexer.rs.html new file mode 100644 index 0000000..a3b777f --- /dev/null +++ b/src/ironcalc_base/formatter/lexer.rs.html @@ -0,0 +1,817 @@ +lexer.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+
pub struct Lexer {
+    position: usize,
+    len: usize,
+    chars: Vec<char>,
+    error_message: String,
+    error_position: usize,
+}
+
+#[derive(PartialEq, Debug)]
+pub enum Token {
+    Color(i32),              // [Red] or [Color 23]
+    Condition(Compare, f64), // [<=100] (Comparator, number)
+    Literal(char), // €, $, (, ), /, :, +, -, ^, ', {, }, <, =, !, ~, > and space or scaped \X
+    Spacer(char),  // *X
+    Ghost(char),   // _X
+    Text(String),  // "Text"
+    Separator,     // ;
+    Raw,           // @
+    Percent,       // %
+    Comma,         // ,
+    Period,        // .
+    Sharp,         // #
+    Zero,          // 0
+    QuestionMark,  // ?
+    Scientific,    // E+
+    ScientificMinus, // E-
+    General,       // General
+    // Dates
+    Day,            // d
+    DayPadded,      // dd
+    DayNameShort,   // ddd
+    DayName,        // dddd+
+    Month,          // m
+    MonthPadded,    // mm
+    MonthNameShort, // mmm
+    MonthName,      // mmmm or mmmmmm+
+    MonthLetter,    // mmmmm
+    YearShort,      // y or yy
+    Year,           // yyy+
+    // TODO: Hours Minutes and Seconds
+    ILLEGAL,
+    EOF,
+}
+
+#[derive(PartialEq, Eq, Debug)]
+pub enum Compare {
+    Equal,
+    LessThan,
+    GreaterThan,
+    LessOrEqualThan,
+    GreaterOrEqualThan,
+}
+
+impl Token {
+    pub fn is_digit(&self) -> bool {
+        (self == &Token::Zero) || (self == &Token::Sharp) || (self == &Token::QuestionMark)
+    }
+
+    pub fn is_date(&self) -> bool {
+        self == &Token::Day
+            || self == &Token::DayPadded
+            || self == &Token::DayNameShort
+            || self == &Token::DayName
+            || self == &Token::MonthName
+            || self == &Token::MonthNameShort
+            || self == &Token::Month
+            || self == &Token::MonthPadded
+            || self == &Token::MonthLetter
+            || self == &Token::YearShort
+            || self == &Token::Year
+    }
+}
+
+impl Lexer {
+    pub fn new(format: &str) -> Lexer {
+        let chars: Vec<char> = format.chars().collect();
+        let len = chars.len();
+        Lexer {
+            chars,
+            position: 0,
+            len,
+            error_message: "".to_string(),
+            error_position: 0,
+        }
+    }
+
+    fn peek_char(&mut self) -> Option<char> {
+        let position = self.position;
+        if position < self.len {
+            Some(self.chars[position])
+        } else {
+            None
+        }
+    }
+
+    fn read_next_char(&mut self) -> Option<char> {
+        let position = self.position;
+        if position < self.len {
+            self.position = position + 1;
+            Some(self.chars[position])
+        } else {
+            None
+        }
+    }
+
+    fn set_error(&mut self, error: &str) {
+        self.error_message = error.to_string();
+        self.error_position = self.position;
+        self.position = self.len;
+    }
+
+    fn consume_string(&mut self) -> Option<String> {
+        let mut position = self.position;
+        let len = self.len;
+        let mut chars = "".to_string();
+        while position < len {
+            let x = self.chars[position];
+            position += 1;
+            if x != '"' {
+                chars.push(x);
+            } else if position < len && self.chars[position] == '"' {
+                chars.push(x);
+                chars.push(self.chars[position]);
+                position += 1;
+            } else {
+                self.position = position;
+                return Some(chars);
+            }
+        }
+        None
+    }
+
+    fn consume_number(&mut self) -> Option<f64> {
+        let mut position = self.position;
+        let len = self.len;
+        let mut chars = "".to_string();
+        // numbers before the '.'
+        while position < len {
+            let x = self.chars[position];
+            if x.is_ascii_digit() {
+                chars.push(x);
+            } else {
+                break;
+            }
+            position += 1;
+        }
+        if position < len && self.chars[position] == '.' {
+            // numbers after the'.'
+            chars.push('.');
+            position += 1;
+            while position < len {
+                let x = self.chars[position];
+                if x.is_ascii_digit() {
+                    chars.push(x);
+                } else {
+                    break;
+                }
+                position += 1;
+            }
+        }
+        if position + 1 < len && self.chars[position] == 'e' {
+            // exponential side
+            let x = self.chars[position + 1];
+            if x == '-' || x == '+' || x.is_ascii_digit() {
+                chars.push('e');
+                chars.push(x);
+                position += 2;
+                while position < len {
+                    let x = self.chars[position];
+                    if x.is_ascii_digit() {
+                        chars.push(x);
+                    } else {
+                        break;
+                    }
+                    position += 1;
+                }
+            }
+        }
+        self.position = position;
+        match chars.parse::<f64>() {
+            Err(_) => None,
+            Ok(v) => Some(v),
+        }
+    }
+
+    fn consume_condition(&mut self) -> Option<(Compare, f64)> {
+        let cmp;
+        match self.read_next_char() {
+            Some('<') => {
+                if let Some('=') = self.peek_char() {
+                    self.read_next_char();
+                    cmp = Compare::LessOrEqualThan;
+                } else {
+                    cmp = Compare::LessThan;
+                }
+            }
+            Some('>') => {
+                if let Some('=') = self.peek_char() {
+                    self.read_next_char();
+                    cmp = Compare::GreaterOrEqualThan;
+                } else {
+                    cmp = Compare::GreaterThan;
+                }
+            }
+            Some('=') => {
+                cmp = Compare::Equal;
+            }
+            _ => {
+                return None;
+            }
+        }
+        if let Some(v) = self.consume_number() {
+            return Some((cmp, v));
+        }
+        None
+    }
+
+    fn consume_color(&mut self) -> Option<i32> {
+        let colors = [
+            "black", "white", "red", "green", "blue", "yellow", "magenta",
+        ];
+        let mut chars = "".to_string();
+        while let Some(ch) = self.read_next_char() {
+            if ch == ']' {
+                if let Some(index) = colors.iter().position(|&x| x == chars.to_lowercase()) {
+                    return Some(index as i32);
+                }
+                if !chars.starts_with("Color") {
+                    return None;
+                }
+                if let Ok(index) = chars[5..].trim().parse::<i32>() {
+                    if index < 57 && index > 0 {
+                        return Some(index);
+                    } else {
+                        return None;
+                    }
+                }
+                return None;
+            } else {
+                chars.push(ch);
+            }
+        }
+        None
+    }
+
+    pub fn peek_token(&mut self) -> Token {
+        let position = self.position;
+        let token = self.next_token();
+        self.position = position;
+        token
+    }
+
+    pub fn next_token(&mut self) -> Token {
+        let ch = self.read_next_char();
+        match ch {
+            Some(x) => match x {
+                '$' | '€' | '(' | ')' | '/' | ':' | '+' | '-' | '^' | '\'' | '{' | '}' | '<'
+                | '=' | '!' | '~' | '>' | ' ' => Token::Literal(x),
+                '?' => Token::QuestionMark,
+                ';' => Token::Separator,
+                '#' => Token::Sharp,
+                ',' => Token::Comma,
+                '.' => Token::Period,
+                '0' => Token::Zero,
+                '@' => Token::Raw,
+                '%' => Token::Percent,
+                '[' => {
+                    if let Some(c) = self.peek_char() {
+                        if c == '<' || c == '>' || c == '=' {
+                            // Condition
+                            if let Some((cmp, value)) = self.consume_condition() {
+                                Token::Condition(cmp, value)
+                            } else {
+                                self.set_error("Failed to parse condition");
+                                Token::ILLEGAL
+                            }
+                        } else {
+                            // Color
+                            if let Some(index) = self.consume_color() {
+                                return Token::Color(index);
+                            }
+                            self.set_error("Failed to parse color");
+                            Token::ILLEGAL
+                        }
+                    } else {
+                        self.set_error("Unexpected end of input");
+                        Token::ILLEGAL
+                    }
+                }
+                '_' => {
+                    if let Some(y) = self.read_next_char() {
+                        Token::Ghost(y)
+                    } else {
+                        self.set_error("Unexpected end of input");
+                        Token::ILLEGAL
+                    }
+                }
+                '*' => {
+                    if let Some(y) = self.read_next_char() {
+                        Token::Spacer(y)
+                    } else {
+                        self.set_error("Unexpected end of input");
+                        Token::ILLEGAL
+                    }
+                }
+                '\\' => {
+                    if let Some(y) = self.read_next_char() {
+                        Token::Literal(y)
+                    } else {
+                        self.set_error("Unexpected end of input");
+                        Token::ILLEGAL
+                    }
+                }
+                '"' => {
+                    if let Some(s) = self.consume_string() {
+                        Token::Text(s)
+                    } else {
+                        self.set_error("Did not find end of text string");
+                        Token::ILLEGAL
+                    }
+                }
+                'E' => {
+                    if let Some(s) = self.read_next_char() {
+                        if s == '+' {
+                            Token::Scientific
+                        } else if s == '-' {
+                            Token::ScientificMinus
+                        } else {
+                            self.set_error(&format!("Unexpected char: {}. Expected + or -", s));
+                            Token::ILLEGAL
+                        }
+                    } else {
+                        self.set_error("Unexpected end of input");
+                        Token::ILLEGAL
+                    }
+                }
+                'd' => {
+                    let mut d = 1;
+                    while let Some('d') = self.peek_char() {
+                        d += 1;
+                        self.read_next_char();
+                    }
+                    match d {
+                        1 => Token::Day,
+                        2 => Token::DayPadded,
+                        3 => Token::DayNameShort,
+                        _ => Token::DayName,
+                    }
+                }
+                'm' => {
+                    let mut m = 1;
+                    while let Some('m') = self.peek_char() {
+                        m += 1;
+                        self.read_next_char();
+                    }
+                    match m {
+                        1 => Token::Month,
+                        2 => Token::MonthPadded,
+                        3 => Token::MonthNameShort,
+                        4 => Token::MonthName,
+                        5 => Token::MonthLetter,
+                        _ => Token::MonthName,
+                    }
+                }
+                'y' => {
+                    let mut y = 1;
+                    while let Some('y') = self.peek_char() {
+                        y += 1;
+                        self.read_next_char();
+                    }
+                    if y == 1 || y == 2 {
+                        Token::YearShort
+                    } else {
+                        Token::Year
+                    }
+                }
+                'g' | 'G' => {
+                    for c in "eneral".chars() {
+                        let cc = self.read_next_char();
+                        if Some(c) != cc {
+                            self.set_error(&format!("Unexpected character: {}", x));
+                            return Token::ILLEGAL;
+                        }
+                    }
+                    Token::General
+                }
+                _ => {
+                    self.set_error(&format!("Unexpected character: {}", x));
+                    Token::ILLEGAL
+                }
+            },
+            None => Token::EOF,
+        }
+    }
+}
+
+pub fn is_likely_date_number_format(format: &str) -> bool {
+    let mut lexer = Lexer::new(format);
+    loop {
+        let token = lexer.next_token();
+        if token == Token::EOF {
+            return false;
+        }
+        if token.is_date() {
+            return true;
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/formatter/mod.rs.html b/src/ironcalc_base/formatter/mod.rs.html new file mode 100644 index 0000000..7430668 --- /dev/null +++ b/src/ironcalc_base/formatter/mod.rs.html @@ -0,0 +1,211 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+
pub mod dates;
+pub mod format;
+pub mod lexer;
+pub mod parser;
+
+#[cfg(test)]
+mod test;
+
+// Excel formatting is extremely tricky and I think implementing all it's rules might be borderline impossible.
+// But the essentials are easy to understand.
+//
+// A general Excel formatting string is divided iun four parts:
+//
+// <POSITIVE>;<NEGATIVE>;<ZERO>;<TEXT>
+//
+// * How many decimal digits do you need?
+//
+// 0.000 for exactly three
+// 0.00??? for at least two and up to five
+//
+// * Do you need a thousands separator?
+//
+// #,##
+// #      will just write the number
+// #,     will write the number up to the thousand separator (if there is nothing else)
+//
+// But #,# and any number of '#' to the right will work just as good. So the following all produce the same results:
+// #,##0.00          #,######0.00            #,0.00
+//
+// For us in IronCalc the most general format string for a number (non-scientific notation) will be:
+//
+// 1. Will have #,## at the beginning if we use the thousand separator
+// 2. Then 0.0* with as many 0 as mandatory decimal places
+// 3. Then ?* with as many question marks as possible decimal places
+//
+// Valid examples:
+// #,##0.???    Thousand separator, up to three decimal digits
+// 0.00         No thousand separator. Two mandatory decimal places
+// 0.0?         No thousand separator. One mandatory decimal digit and one extra if present.
+//
+// * Do you what the text in color?
+//
+// Use [RED] or any color in https://www.excelsupersite.com/what-are-the-56-colorindex-colors-in-excel/
+
+// Weird things
+// ============
+//
+// ####0.0E+00 of 12345467.890123 (changing the number of '#' produces results I do not understand)
+// ?www??.????0220000 will format 1234567.890123 to 12345www67.89012223000
+//
+// Things we will not implement
+// ============================
+//
+// 1.- The accounting format can leave white spaces of the size of a particular character. For instance:
+//
+//   #,##0.00_);[Red](#,##0.00)
+//
+// Will leave a white space to the right of positive numbers so that they are always aligned with negative numbers
+//
+// 2.- Excel can repeat a character as many times as needed to fill the cell:
+//
+// _($* #,##0_);_($* (#,##0))
+//
+// This will put a '$' sign to the left most (leaving a space the size of '(') and then as many empty spaces as possible
+// and then the number:
+//  | $      234 |
+//  | $     1234 |
+// We can't do this easily in IronCalc
+//
+// 3.- You can use ?/? to format fractions in Excel (this is probably not too hard)
+
+// TOKENs
+// ======
+//
+// * Color [Red] or [Color 23] or [Color23]
+// * Conditions [<100]
+// * Space _X when X is any given char
+// * A spacer of chars: *X where X is repeated as much as possible
+// * Literals: $, (, ), :, +, - and space
+// * Text: "Some Text"
+// * Escaped char: \X where X is anything
+// * % appears literal and multiplies number by 100
+// * , If it's in between digit characters it uses the thousand separator. If it is after the digit characters it multiplies by 1000
+// * Digit characters: 0, #, ?
+// * ; Types formatter divider
+// * @ inserts raw text
+// * Scientific literals E+, E-, e+, e-
+// * . period. First one is the decimal point, subsequent are literals.
+
+// d day of the month
+// dd day of the month (padded i.e 05)
+// ddd day of the week abbreviation
+// dddd+ day of the week
+// mmm Abbreviation month
+// mmmm Month name
+// mmmmm First letter of the month
+// y or yy 2-digit year
+// yyy+ 4 digit year
+
+// References
+// ==========
+//
+// [1] https://support.microsoft.com/en-us/office/number-format-codes-5026bbd6-04bc-48cd-bf33-80f18b4eae68?ui=en-us&rs=en-us&ad=us
+// [2] https://developers.google.com/sheets/api/guides/formats
+// [3] https://docs.microsoft.com/en-us/openspecs/office_standards/ms-oe376/0e59abdb-7f4e-48fc-9b89-67832fa11789
+
\ No newline at end of file diff --git a/src/ironcalc_base/formatter/parser.rs.html b/src/ironcalc_base/formatter/parser.rs.html new file mode 100644 index 0000000..f350077 --- /dev/null +++ b/src/ironcalc_base/formatter/parser.rs.html @@ -0,0 +1,595 @@ +parser.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+
use super::lexer::{Compare, Lexer, Token};
+
+pub struct Digit {
+    pub kind: char, // '#' | '?' | '0'
+    pub index: i32,
+    pub number: char, // 'i' | 'd' | 'e' (integer, decimal or exponent)
+}
+
+pub enum TextToken {
+    Literal(char),
+    Text(String),
+    Ghost(char),
+    Spacer(char),
+    // Text
+    Raw,
+    Digit(Digit),
+    Period,
+    // Dates
+    Day,
+    DayPadded,
+    DayNameShort,
+    DayName,
+    Month,
+    MonthPadded,
+    MonthNameShort,
+    MonthName,
+    MonthLetter,
+    YearShort,
+    Year,
+}
+pub struct NumberPart {
+    pub color: Option<i32>,
+    pub condition: Option<(Compare, f64)>,
+    pub use_thousands: bool,
+    pub percent: i32, // multiply number by 100^percent
+    pub comma: i32,   // divide number by 1000^comma
+    pub tokens: Vec<TextToken>,
+    pub digit_count: i32, // number of digit tokens (#, 0 or ?) to the left of the decimal point
+    pub precision: i32,   // number of digits to the right of the decimal point
+    pub is_scientific: bool,
+    pub scientific_minus: bool,
+    pub exponent_digit_count: i32,
+}
+
+pub struct DatePart {
+    pub color: Option<i32>,
+    pub tokens: Vec<TextToken>,
+}
+
+pub struct ErrorPart {}
+
+pub struct GeneralPart {}
+
+pub enum ParsePart {
+    Number(NumberPart),
+    Date(DatePart),
+    Error(ErrorPart),
+    General(GeneralPart),
+}
+
+pub struct Parser {
+    pub parts: Vec<ParsePart>,
+    lexer: Lexer,
+}
+
+impl ParsePart {
+    pub fn is_error(&self) -> bool {
+        match &self {
+            ParsePart::Date(..) => false,
+            ParsePart::Number(..) => false,
+            ParsePart::Error(..) => true,
+            ParsePart::General(..) => false,
+        }
+    }
+    pub fn is_date(&self) -> bool {
+        match &self {
+            ParsePart::Date(..) => true,
+            ParsePart::Number(..) => false,
+            ParsePart::Error(..) => false,
+            ParsePart::General(..) => false,
+        }
+    }
+}
+
+impl Parser {
+    pub fn new(format: &str) -> Self {
+        let lexer = Lexer::new(format);
+        let parts = vec![];
+        Parser { parts, lexer }
+    }
+    pub fn parse(&mut self) {
+        while self.lexer.peek_token() != Token::EOF {
+            let part = self.parse_part();
+            self.parts.push(part);
+        }
+    }
+
+    fn parse_part(&mut self) -> ParsePart {
+        let mut token = self.lexer.next_token();
+        let mut digit_count = 0;
+        let mut precision = 0;
+        let mut is_date = false;
+        let mut is_number = false;
+        let mut found_decimal_dot = false;
+        let mut use_thousands = false;
+        let mut comma = 0;
+        let mut percent = 0;
+        let mut last_token_is_digit = false;
+        let mut color = None;
+        let mut condition = None;
+        let mut tokens = vec![];
+        let mut is_scientific = false;
+        let mut scientific_minus = false;
+        let mut exponent_digit_count = 0;
+        let mut number = 'i';
+        let mut index = 0;
+
+        while token != Token::EOF && token != Token::Separator {
+            let next_token = self.lexer.next_token();
+            let token_is_digit = token.is_digit();
+            is_number = is_number || token_is_digit;
+            let next_token_is_digit = next_token.is_digit();
+            if token_is_digit {
+                if is_scientific {
+                    exponent_digit_count += 1;
+                } else if found_decimal_dot {
+                    precision += 1;
+                } else {
+                    digit_count += 1;
+                }
+            }
+            match token {
+                Token::General => {
+                    if tokens.is_empty() {
+                        return ParsePart::General(GeneralPart {});
+                    } else {
+                        return ParsePart::Error(ErrorPart {});
+                    }
+                }
+                Token::Comma => {
+                    // If it is in between digit token then we use the thousand separator
+                    if last_token_is_digit && next_token_is_digit {
+                        use_thousands = true;
+                    } else if digit_count > 0 {
+                        comma += 1;
+                    } else {
+                        // Before the number is just a literal.
+                        tokens.push(TextToken::Literal(','));
+                    }
+                }
+                Token::Percent => {
+                    tokens.push(TextToken::Literal('%'));
+                    percent += 1;
+                }
+                Token::Period => {
+                    if !found_decimal_dot {
+                        tokens.push(TextToken::Period);
+                        found_decimal_dot = true;
+                        if number == 'i' {
+                            number = 'd';
+                            index = 0;
+                        }
+                    } else {
+                        tokens.push(TextToken::Literal('.'));
+                    }
+                }
+                Token::Color(index) => {
+                    color = Some(index);
+                }
+                Token::Condition(cmp, value) => {
+                    condition = Some((cmp, value));
+                }
+                Token::QuestionMark => {
+                    tokens.push(TextToken::Digit(Digit {
+                        kind: '?',
+                        index,
+                        number,
+                    }));
+                    index += 1;
+                }
+                Token::Sharp => {
+                    tokens.push(TextToken::Digit(Digit {
+                        kind: '#',
+                        index,
+                        number,
+                    }));
+                    index += 1;
+                }
+                Token::Zero => {
+                    tokens.push(TextToken::Digit(Digit {
+                        kind: '0',
+                        index,
+                        number,
+                    }));
+                    index += 1;
+                }
+                Token::Literal(value) => {
+                    tokens.push(TextToken::Literal(value));
+                }
+                Token::Text(value) => {
+                    tokens.push(TextToken::Text(value));
+                }
+                Token::Ghost(value) => {
+                    tokens.push(TextToken::Ghost(value));
+                }
+                Token::Spacer(value) => {
+                    tokens.push(TextToken::Spacer(value));
+                }
+                Token::Day => {
+                    is_date = true;
+                    tokens.push(TextToken::Day);
+                }
+                Token::DayPadded => {
+                    is_date = true;
+                    tokens.push(TextToken::DayPadded);
+                }
+                Token::DayNameShort => {
+                    is_date = true;
+                    tokens.push(TextToken::DayNameShort);
+                }
+                Token::DayName => {
+                    is_date = true;
+                    tokens.push(TextToken::DayName);
+                }
+                Token::MonthNameShort => {
+                    is_date = true;
+                    tokens.push(TextToken::MonthNameShort);
+                }
+                Token::MonthName => {
+                    is_date = true;
+                    tokens.push(TextToken::MonthName);
+                }
+                Token::Month => {
+                    is_date = true;
+                    tokens.push(TextToken::Month);
+                }
+                Token::MonthPadded => {
+                    is_date = true;
+                    tokens.push(TextToken::MonthPadded);
+                }
+                Token::MonthLetter => {
+                    is_date = true;
+                    tokens.push(TextToken::MonthLetter);
+                }
+                Token::YearShort => {
+                    is_date = true;
+                    tokens.push(TextToken::YearShort);
+                }
+                Token::Year => {
+                    is_date = true;
+                    tokens.push(TextToken::Year);
+                }
+                Token::Scientific => {
+                    if !is_scientific {
+                        index = 0;
+                        number = 'e';
+                    }
+                    is_scientific = true;
+                }
+                Token::ScientificMinus => {
+                    is_scientific = true;
+                    scientific_minus = true;
+                }
+                Token::Separator => {}
+                Token::Raw => {
+                    tokens.push(TextToken::Raw);
+                }
+                Token::ILLEGAL => {
+                    return ParsePart::Error(ErrorPart {});
+                }
+                Token::EOF => {}
+            }
+            last_token_is_digit = token_is_digit;
+            token = next_token;
+        }
+        if is_date {
+            if is_number {
+                return ParsePart::Error(ErrorPart {});
+            }
+            ParsePart::Date(DatePart { color, tokens })
+        } else {
+            ParsePart::Number(NumberPart {
+                color,
+                condition,
+                use_thousands,
+                percent,
+                comma,
+                tokens,
+                digit_count,
+                precision,
+                is_scientific,
+                scientific_minus,
+                exponent_digit_count,
+            })
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/binary_search.rs.html b/src/ironcalc_base/functions/binary_search.rs.html new file mode 100644 index 0000000..8253867 --- /dev/null +++ b/src/ironcalc_base/functions/binary_search.rs.html @@ -0,0 +1,421 @@ +binary_search.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+
use std::cmp::Ordering;
+
+use crate::{
+    calc_result::{CalcResult, CellReference},
+    model::Model,
+};
+
+use super::util::compare_values;
+
+// NOTE: We don't know how Excel exactly implements binary search internally.
+// This means that if the values on the lookup range are not in order our results and Excel's will differ
+
+// Assumes values are in ascending order, returns matching index or the largest value smaller than target.
+// Returns None if target is smaller than the smaller value.
+pub(crate) fn binary_search_or_smaller<T: Ord>(target: &T, array: &[T]) -> Option<i32> {
+    // We apply binary search leftmost for value in the range
+    let n = array.len();
+    let mut l = 0;
+    let mut r = n;
+    while l < r {
+        let m = (l + r) / 2;
+        if &array[m] < target {
+            l = m + 1;
+        } else {
+            r = m;
+        }
+    }
+    if l == n {
+        return Some((l - 1) as i32);
+    }
+    // Now l points to the leftmost element
+    if &array[l] == target {
+        return Some(l as i32);
+    }
+    // If target is less than the minimum return None
+    if l == 0 {
+        return None;
+    }
+    Some((l - 1) as i32)
+}
+
+// Assumes values are in ascending order, returns matching index or the smaller value larger than target.
+// Returns None if target is smaller than the smaller value.
+pub(crate) fn binary_search_or_greater<T: Ord>(target: &T, array: &[T]) -> Option<i32> {
+    let mut l = 0;
+    let mut r = array.len();
+    while l < r {
+        let mut m = (l + r) / 2;
+        match &array[m].cmp(target) {
+            Ordering::Less => {
+                l = m + 1;
+            }
+            Ordering::Greater => {
+                r = m;
+            }
+            Ordering::Equal => {
+                while m > 1 {
+                    if &array[m - 1] == target {
+                        m -= 1;
+                    } else {
+                        break;
+                    }
+                }
+                return Some(m as i32);
+            }
+        }
+    }
+    // If target is larger than the maximum return None
+    if r == array.len() {
+        return None;
+    }
+    // Now r points to the rightmost element
+    Some(r as i32)
+}
+
+// Assumes values are in descending order
+pub(crate) fn binary_search_descending_or_smaller<T: Ord>(target: &T, array: &[T]) -> Option<i32> {
+    let n = array.len();
+    let mut l = 0;
+    let mut r = n;
+    while l < r {
+        let m = (l + r) / 2;
+        let mut index = n - m - 1;
+        match &array[index].cmp(target) {
+            Ordering::Less => {
+                l = m + 1;
+            }
+            Ordering::Greater => {
+                r = m;
+            }
+            Ordering::Equal => {
+                while index < n - 1 {
+                    if &array[index + 1] == target {
+                        index += 1;
+                    } else {
+                        break;
+                    }
+                }
+                return Some(index as i32);
+            }
+        }
+    }
+    if l == 0 {
+        return None;
+    }
+    Some((n - l) as i32)
+}
+
+// Assumes values are in descending order, returns matching index or the smaller value larger than target.
+// Returns None if target is smaller than the smaller value.
+pub(crate) fn binary_search_descending_or_greater<T: Ord>(target: &T, array: &[T]) -> Option<i32> {
+    let n = array.len();
+    let mut l = 0;
+    let mut r = n;
+    while l < r {
+        let m = (l + r) / 2;
+        let mut index = n - m - 1;
+        match &array[index].cmp(target) {
+            Ordering::Less => {
+                l = m + 1;
+            }
+            Ordering::Greater => {
+                r = m;
+            }
+            Ordering::Equal => {
+                while index < n - 1 {
+                    if &array[index + 1] == target {
+                        index += 1;
+                    } else {
+                        break;
+                    }
+                }
+                return Some(index as i32);
+            }
+        }
+    }
+    if r == n {
+        return None;
+    }
+    Some((n - r - 1) as i32)
+}
+
+impl Model {
+    /// Returns an array with the list of cell values in the range
+    pub(crate) fn prepare_array(
+        &mut self,
+        left: &CellReference,
+        right: &CellReference,
+        is_row_vector: bool,
+    ) -> Vec<CalcResult> {
+        let n = if is_row_vector {
+            right.row - left.row
+        } else {
+            right.column - left.column
+        } + 1;
+        let mut result = vec![];
+        for index in 0..n {
+            let row;
+            let column;
+            if is_row_vector {
+                row = left.row + index;
+                column = left.column;
+            } else {
+                column = left.column + index;
+                row = left.row;
+            }
+            let value = self.evaluate_cell(CellReference {
+                sheet: left.sheet,
+                row,
+                column,
+            });
+            result.push(value);
+        }
+        result
+    }
+
+    /// Old style binary search. Used in HLOOKUP, etc
+    pub(crate) fn binary_search(
+        &mut self,
+        target: &CalcResult,
+        left: &CellReference,
+        right: &CellReference,
+        is_row_vector: bool,
+    ) -> i32 {
+        let array = self.prepare_array(left, right, is_row_vector);
+        // We apply binary search leftmost for value in the range
+        let mut l = 0;
+        let mut r = array.len();
+        while l < r {
+            let m = (l + r) / 2;
+            match compare_values(&array[m], target) {
+                -1 => {
+                    l = m + 1;
+                }
+                1 => {
+                    r = m;
+                }
+                _ => {
+                    return m as i32;
+                }
+            }
+        }
+        // If target is less than the minimum return #N/A
+        if l == 0 {
+            return -2;
+        }
+        // Now l points to the leftmost element
+        (l - 1) as i32
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/date_and_time.rs.html b/src/ironcalc_base/functions/date_and_time.rs.html new file mode 100644 index 0000000..ec1a16c --- /dev/null +++ b/src/ironcalc_base/functions/date_and_time.rs.html @@ -0,0 +1,629 @@ +date_and_time.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+
use chrono::Datelike;
+use chrono::Months;
+use chrono::NaiveDateTime;
+use chrono::TimeZone;
+use chrono::Timelike;
+
+use crate::formatter::dates::date_to_serial_number;
+use crate::model::get_milliseconds_since_epoch;
+use crate::{
+    calc_result::{CalcResult, CellReference},
+    constants::EXCEL_DATE_BASE,
+    expressions::parser::Node,
+    expressions::token::Error,
+    formatter::dates::from_excel_date,
+    model::Model,
+};
+
+impl Model {
+    pub(crate) fn fn_day(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let args_count = args.len();
+        if args_count != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let serial_number = match self.get_number(&args[0], cell) {
+            Ok(c) => {
+                let t = c.floor() as i64;
+                if t < 0 {
+                    return CalcResult::Error {
+                        error: Error::NUM,
+                        origin: cell,
+                        message: "Function DAY parameter 1 value is negative. It should be positive or zero.".to_string(),
+                    };
+                }
+                t
+            }
+            Err(s) => return s,
+        };
+        let date = from_excel_date(serial_number);
+        let day = date.day() as f64;
+        CalcResult::Number(day)
+    }
+
+    pub(crate) fn fn_month(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let args_count = args.len();
+        if args_count != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let serial_number = match self.get_number(&args[0], cell) {
+            Ok(c) => {
+                let t = c.floor() as i64;
+                if t < 0 {
+                    return CalcResult::Error {
+                        error: Error::NUM,
+                        origin: cell,
+                        message: "Function MONTH parameter 1 value is negative. It should be positive or zero.".to_string(),
+                    };
+                }
+                t
+            }
+            Err(s) => return s,
+        };
+        let date = from_excel_date(serial_number);
+        let month = date.month() as f64;
+        CalcResult::Number(month)
+    }
+
+    pub(crate) fn fn_eomonth(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let args_count = args.len();
+        if args_count != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let serial_number = match self.get_number(&args[0], cell) {
+            Ok(c) => {
+                let t = c.floor() as i64;
+                if t < 0 {
+                    return CalcResult::Error {
+                        error: Error::NUM,
+                        origin: cell,
+                        message: "Function EOMONTH parameter 1 value is negative. It should be positive or zero.".to_string(),
+                    };
+                }
+                t
+            }
+            Err(s) => return s,
+        };
+
+        let months = match self.get_number_no_bools(&args[1], cell) {
+            Ok(c) => {
+                let t = c.trunc();
+                t as i32
+            }
+            Err(s) => return s,
+        };
+
+        let months_abs = months.unsigned_abs();
+
+        let native_date = if months > 0 {
+            from_excel_date(serial_number) + Months::new(months_abs)
+        } else {
+            from_excel_date(serial_number) - Months::new(months_abs)
+        };
+
+        // Instead of calculating the end of month we compute the first day of the following month
+        // and take one day.
+        let mut month = native_date.month() + 1;
+        let mut year = native_date.year();
+        if month == 13 {
+            month = 1;
+            year += 1;
+        }
+        match date_to_serial_number(1, month, year) {
+            Ok(serial_number) => CalcResult::Number(serial_number as f64 - 1.0),
+            Err(message) => CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message,
+            },
+        }
+    }
+
+    // year, month, day
+    pub(crate) fn fn_date(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let args_count = args.len();
+        if args_count != 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let year = match self.get_number(&args[0], cell) {
+            Ok(c) => {
+                let t = c.floor() as i32;
+                if t < 0 {
+                    return CalcResult::Error {
+                        error: Error::NUM,
+                        origin: cell,
+                        message: "Out of range parameters for date".to_string(),
+                    };
+                }
+                t
+            }
+            Err(s) => return s,
+        };
+        let month = match self.get_number(&args[1], cell) {
+            Ok(c) => {
+                let t = c.floor();
+                if t < 0.0 {
+                    return CalcResult::Error {
+                        error: Error::NUM,
+                        origin: cell,
+                        message: "Out of range parameters for date".to_string(),
+                    };
+                }
+                t as u32
+            }
+            Err(s) => return s,
+        };
+        let day = match self.get_number(&args[2], cell) {
+            Ok(c) => {
+                let t = c.floor();
+                if t < 0.0 {
+                    return CalcResult::Error {
+                        error: Error::NUM,
+                        origin: cell,
+                        message: "Out of range parameters for date".to_string(),
+                    };
+                }
+                t as u32
+            }
+            Err(s) => return s,
+        };
+        match date_to_serial_number(day, month, year) {
+            Ok(serial_number) => CalcResult::Number(serial_number as f64),
+            Err(message) => CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message,
+            },
+        }
+    }
+
+    pub(crate) fn fn_year(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let args_count = args.len();
+        if args_count != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let serial_number = match self.get_number(&args[0], cell) {
+            Ok(c) => {
+                let t = c.floor() as i64;
+                if t < 0 {
+                    return CalcResult::Error {
+                        error: Error::NUM,
+                        origin: cell,
+                        message: "Function YEAR parameter 1 value is negative. It should be positive or zero.".to_string(),
+                    };
+                }
+                t
+            }
+            Err(s) => return s,
+        };
+        let date = from_excel_date(serial_number);
+        let year = date.year() as f64;
+        CalcResult::Number(year)
+    }
+
+    // date, months
+    pub(crate) fn fn_edate(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let args_count = args.len();
+        if args_count != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let serial_number = match self.get_number(&args[0], cell) {
+            Ok(c) => {
+                let t = c.floor() as i64;
+                if t < 0 {
+                    return CalcResult::Error {
+                        error: Error::NUM,
+                        origin: cell,
+                        message: "Parameter 1 value is negative. It should be positive or zero."
+                            .to_string(),
+                    };
+                }
+                t
+            }
+            Err(s) => return s,
+        };
+
+        let months = match self.get_number(&args[1], cell) {
+            Ok(c) => {
+                let t = c.trunc();
+                t as i32
+            }
+            Err(s) => return s,
+        };
+
+        let months_abs = months.unsigned_abs();
+
+        let native_date = if months > 0 {
+            from_excel_date(serial_number) + Months::new(months_abs)
+        } else {
+            from_excel_date(serial_number) - Months::new(months_abs)
+        };
+
+        let serial_number = native_date.num_days_from_ce() - EXCEL_DATE_BASE;
+        if serial_number < 0 {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "EDATE out of bounds".to_string(),
+            };
+        }
+        CalcResult::Number(serial_number as f64)
+    }
+
+    pub(crate) fn fn_today(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let args_count = args.len();
+        if args_count != 0 {
+            return CalcResult::Error {
+                error: Error::ERROR,
+                origin: cell,
+                message: "Wrong number of arguments".to_string(),
+            };
+        }
+        // milliseconds since January 1, 1970 00:00:00 UTC.
+        let milliseconds = get_milliseconds_since_epoch();
+        let seconds = milliseconds / 1000;
+        let dt = match NaiveDateTime::from_timestamp_opt(seconds, 0) {
+            Some(dt) => dt,
+            None => {
+                return CalcResult::Error {
+                    error: Error::ERROR,
+                    origin: cell,
+                    message: "Invalid date".to_string(),
+                }
+            }
+        };
+        let local_time = self.tz.from_utc_datetime(&dt);
+        // 693_594 is computed as:
+        // NaiveDate::from_ymd(1900, 1, 1).num_days_from_ce() - 2
+        // The 2 days offset is because of Excel 1900 bug
+        let days_from_1900 = local_time.num_days_from_ce() - 693_594;
+
+        CalcResult::Number(days_from_1900 as f64)
+    }
+
+    pub(crate) fn fn_now(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let args_count = args.len();
+        if args_count != 0 {
+            return CalcResult::Error {
+                error: Error::ERROR,
+                origin: cell,
+                message: "Wrong number of arguments".to_string(),
+            };
+        }
+        // milliseconds since January 1, 1970 00:00:00 UTC.
+        let milliseconds = get_milliseconds_since_epoch();
+        let seconds = milliseconds / 1000;
+        let dt = match NaiveDateTime::from_timestamp_opt(seconds, 0) {
+            Some(dt) => dt,
+            None => {
+                return CalcResult::Error {
+                    error: Error::ERROR,
+                    origin: cell,
+                    message: "Invalid date".to_string(),
+                }
+            }
+        };
+        let local_time = self.tz.from_utc_datetime(&dt);
+        // 693_594 is computed as:
+        // NaiveDate::from_ymd(1900, 1, 1).num_days_from_ce() - 2
+        // The 2 days offset is because of Excel 1900 bug
+        let days_from_1900 = local_time.num_days_from_ce() - 693_594;
+        let days = (local_time.num_seconds_from_midnight() as f64) / (60.0 * 60.0 * 24.0);
+
+        CalcResult::Number(days_from_1900 as f64 + days.fract())
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/bessel.rs.html b/src/ironcalc_base/functions/engineering/bessel.rs.html new file mode 100644 index 0000000..1049987 --- /dev/null +++ b/src/ironcalc_base/functions/engineering/bessel.rs.html @@ -0,0 +1,353 @@ +bessel.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+
use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::{parser::Node, token::Error},
+    model::Model,
+};
+
+use super::transcendental::{bessel_i, bessel_j, bessel_k, bessel_y, erf};
+// https://root.cern/doc/v610/TMath_8cxx_source.html
+
+// Notice that the parameters for Bessel functions in Excel and here have inverted order
+// EXCEL_BESSEL(x, n) => bessel(n, x)
+
+impl Model {
+    pub(crate) fn fn_besseli(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let x = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let n = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let n = n.trunc() as i32;
+        let result = bessel_i(n, x);
+        if result.is_infinite() || result.is_nan() {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid parameter for Bessel function".to_string(),
+            };
+        }
+        CalcResult::Number(result)
+    }
+    pub(crate) fn fn_besselj(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let x = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let n = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let n = n.trunc() as i32;
+        if n < 0 {
+            // In Excel this ins #NUM!
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid parameter for Bessel function".to_string(),
+            };
+        }
+        let result = bessel_j(n, x);
+        if result.is_infinite() || result.is_nan() {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid parameter for Bessel function".to_string(),
+            };
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_besselk(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let x = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let n = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let n = n.trunc() as i32;
+        let result = bessel_k(n, x);
+        if result.is_infinite() || result.is_nan() {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid parameter for Bessel function".to_string(),
+            };
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_bessely(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let x = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let n = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let n = n.trunc() as i32;
+        if n < 0 {
+            // In Excel this ins #NUM!
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid parameter for Bessel function".to_string(),
+            };
+        }
+        let result = bessel_y(n, x);
+        if result.is_infinite() || result.is_nan() {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid parameter for Bessel function".to_string(),
+            };
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_erf(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(1..=2).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let x = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if args.len() == 2 {
+            let y = match self.get_number_no_bools(&args[1], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            };
+            CalcResult::Number(erf(y) - erf(x))
+        } else {
+            CalcResult::Number(erf(x))
+        }
+    }
+
+    pub(crate) fn fn_erfprecise(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        };
+        let x = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        CalcResult::Number(erf(x))
+    }
+
+    pub(crate) fn fn_erfc(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        };
+        let x = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        CalcResult::Number(1.0 - erf(x))
+    }
+
+    pub(crate) fn fn_erfcprecise(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        };
+        let x = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        CalcResult::Number(1.0 - erf(x))
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/bit_operations.rs.html b/src/ironcalc_base/functions/engineering/bit_operations.rs.html new file mode 100644 index 0000000..0d09400 --- /dev/null +++ b/src/ironcalc_base/functions/engineering/bit_operations.rs.html @@ -0,0 +1,467 @@ +bit_operations.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+
use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::parser::Node,
+    expressions::token::Error,
+    model::Model,
+};
+
+// 2^48-1
+const MAX: f64 = 281474976710655.0;
+
+impl Model {
+    // BITAND( number1, number2)
+    pub(crate) fn fn_bitand(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let number1 = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let number2 = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if number1.trunc() != number1 || number2.trunc() != number2 {
+            return CalcResult::new_error(Error::NUM, cell, "numbers must be integers".to_string());
+        }
+        if number1 < 0.0 || number2 < 0.0 {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "numbers must be positive or zero".to_string(),
+            );
+        }
+
+        if number1 > MAX || number2 > MAX {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "numbers must be less than 2^48-1".to_string(),
+            );
+        }
+
+        let number1 = number1.trunc() as i64;
+        let number2 = number2.trunc() as i64;
+        let result = number1 & number2;
+        CalcResult::Number(result as f64)
+    }
+
+    // BITOR(number1, number2)
+    pub(crate) fn fn_bitor(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let number1 = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let number2 = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if number1.trunc() != number1 || number2.trunc() != number2 {
+            return CalcResult::new_error(Error::NUM, cell, "numbers must be integers".to_string());
+        }
+        if number1 < 0.0 || number2 < 0.0 {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "numbers must be positive or zero".to_string(),
+            );
+        }
+
+        if number1 > MAX || number2 > MAX {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "numbers must be less than 2^48-1".to_string(),
+            );
+        }
+
+        let number1 = number1.trunc() as i64;
+        let number2 = number2.trunc() as i64;
+        let result = number1 | number2;
+        CalcResult::Number(result as f64)
+    }
+
+    // BITXOR(number1, number2)
+    pub(crate) fn fn_bitxor(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let number1 = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let number2 = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if number1.trunc() != number1 || number2.trunc() != number2 {
+            return CalcResult::new_error(Error::NUM, cell, "numbers must be integers".to_string());
+        }
+        if number1 < 0.0 || number2 < 0.0 {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "numbers must be positive or zero".to_string(),
+            );
+        }
+
+        if number1 > MAX || number2 > MAX {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "numbers must be less than 2^48-1".to_string(),
+            );
+        }
+
+        let number1 = number1.trunc() as i64;
+        let number2 = number2.trunc() as i64;
+        let result = number1 ^ number2;
+        CalcResult::Number(result as f64)
+    }
+
+    // BITLSHIFT(number, shift_amount)
+    pub(crate) fn fn_bitlshift(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let number = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let shift = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if number.trunc() != number {
+            return CalcResult::new_error(Error::NUM, cell, "numbers must be integers".to_string());
+        }
+        if number < 0.0 {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "numbers must be positive or zero".to_string(),
+            );
+        }
+
+        if number > MAX {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "numbers must be less than 2^48-1".to_string(),
+            );
+        }
+
+        if shift.abs() > 53.0 {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "shift amount must be less than 53".to_string(),
+            );
+        }
+
+        let number = number.trunc() as i64;
+        let shift = shift.trunc() as i64;
+        let result = if shift > 0 {
+            number << shift
+        } else {
+            number >> -shift
+        };
+        let result = result as f64;
+        if result.abs() > MAX {
+            return CalcResult::new_error(Error::NUM, cell, "BITLSHIFT overflow".to_string());
+        }
+        CalcResult::Number(result)
+    }
+
+    // BITRSHIFT(number, shift_amount)
+    pub(crate) fn fn_bitrshift(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let number = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let shift = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if number.trunc() != number {
+            return CalcResult::new_error(Error::NUM, cell, "numbers must be integers".to_string());
+        }
+        if number < 0.0 {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "numbers must be positive or zero".to_string(),
+            );
+        }
+
+        if number > MAX {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "numbers must be less than 2^48-1".to_string(),
+            );
+        }
+
+        if shift.abs() > 53.0 {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "shift amount must be less than 53".to_string(),
+            );
+        }
+
+        let number = number.trunc() as i64;
+        let shift = shift.trunc() as i64;
+        let result = if shift > 0 {
+            number >> shift
+        } else {
+            number << -shift
+        };
+        let result = result as f64;
+        if result.abs() > MAX {
+            return CalcResult::new_error(Error::NUM, cell, "BITRSHIFT overflow".to_string());
+        }
+        CalcResult::Number(result)
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/complex.rs.html b/src/ironcalc_base/functions/engineering/complex.rs.html new file mode 100644 index 0000000..b841723 --- /dev/null +++ b/src/ironcalc_base/functions/engineering/complex.rs.html @@ -0,0 +1,1587 @@ +complex.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+
use std::fmt;
+
+use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::{
+        lexer::util::get_tokens,
+        parser::Node,
+        token::{Error, OpSum, TokenType},
+    },
+    model::Model,
+    number_format::to_precision,
+};
+
+/// This implements all functions with complex arguments in the standard
+/// NOTE: If performance is ever needed we should have a new entry in CalcResult,
+/// So this functions will return CalcResult::Complex(x,y, Suffix)
+/// and not having to parse it over and over again.
+
+#[derive(PartialEq, Debug)]
+enum Suffix {
+    I,
+    J,
+}
+
+impl fmt::Display for Suffix {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Suffix::I => write!(f, "i"),
+            Suffix::J => write!(f, "j"),
+        }
+    }
+}
+
+struct Complex {
+    x: f64,
+    y: f64,
+    suffix: Suffix,
+}
+
+impl fmt::Display for Complex {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let x = to_precision(self.x, 15);
+        let y = to_precision(self.y, 15);
+        let suffix = &self.suffix;
+        // it is a bit weird what Excel does but it seems it uses general notation for
+        // numbers > 1e-20 and scientific notation for the rest
+        let y_str = if y.abs() <= 9e-20 {
+            format!("{:E}", y)
+        } else if y == 1.0 {
+            "".to_string()
+        } else if y == -1.0 {
+            "-".to_string()
+        } else {
+            format!("{}", y)
+        };
+        let x_str = if x.abs() <= 9e-20 {
+            format!("{:E}", x)
+        } else {
+            format!("{}", x)
+        };
+        if y == 0.0 && x == 0.0 {
+            write!(f, "0")
+        } else if y == 0.0 {
+            write!(f, "{x_str}")
+        } else if x == 0.0 {
+            write!(f, "{y_str}{suffix}")
+        } else if y > 0.0 {
+            write!(f, "{x_str}+{y_str}{suffix}")
+        } else {
+            write!(f, "{x_str}{y_str}{suffix}")
+        }
+    }
+}
+
+fn parse_complex_number(s: &str) -> Result<(f64, f64, Suffix), String> {
+    // Check for i, j, -i, -j
+    let (sign, s) = match s.strip_prefix('-') {
+        Some(r) => (-1.0, r),
+        None => (1.0, s),
+    };
+    match s {
+        "i" => return Ok((0.0, sign * 1.0, Suffix::I)),
+        "j" => return Ok((0.0, sign * 1.0, Suffix::J)),
+        _ => {
+            // Let it go
+        }
+    };
+
+    // TODO: This is an overuse
+    let tokens = get_tokens(s);
+
+    // There has to be 1, 2 3, or 4 tokens
+    // number
+    // number suffix
+    // number1+suffix
+    // number1+number2 suffix
+
+    match tokens.len() {
+        1 => {
+            // Real number
+            let number1 = match tokens[0].token {
+                TokenType::Number(f) => f,
+                _ => return Err(format!("Not a complex number: {s}")),
+            };
+            // i is the default
+            Ok((sign * number1, 0.0, Suffix::I))
+        }
+        2 => {
+            // number2 i
+            let number2 = match tokens[0].token {
+                TokenType::Number(f) => f,
+                _ => return Err(format!("Not a complex number: {s}")),
+            };
+            let suffix = match &tokens[1].token {
+                TokenType::Ident(w) => match w.as_str() {
+                    "i" => Suffix::I,
+                    "j" => Suffix::J,
+                    _ => return Err(format!("Not a complex number: {s}")),
+                },
+                _ => {
+                    return Err(format!("Not a complex number: {s}"));
+                }
+            };
+            Ok((0.0, sign * number2, suffix))
+        }
+        3 => {
+            let number1 = match tokens[0].token {
+                TokenType::Number(f) => f,
+                _ => return Err(format!("Not a complex number: {s}")),
+            };
+            let operation = match &tokens[1].token {
+                TokenType::Addition(f) => f,
+                _ => return Err(format!("Not a complex number: {s}")),
+            };
+            let suffix = match &tokens[2].token {
+                TokenType::Ident(w) => match w.as_str() {
+                    "i" => Suffix::I,
+                    "j" => Suffix::J,
+                    _ => return Err(format!("Not a complex number: {s}")),
+                },
+                _ => {
+                    return Err(format!("Not a complex number: {s}"));
+                }
+            };
+            let number2 = if matches!(operation, OpSum::Minus) {
+                -1.0
+            } else {
+                1.0
+            };
+            Ok((sign * number1, number2, suffix))
+        }
+        4 => {
+            let number1 = match tokens[0].token {
+                TokenType::Number(f) => f,
+                _ => return Err(format!("Not a complex number: {s}")),
+            };
+            let operation = match &tokens[1].token {
+                TokenType::Addition(f) => f,
+                _ => return Err(format!("Not a complex number: {s}")),
+            };
+            let mut number2 = match tokens[2].token {
+                TokenType::Number(f) => f,
+                _ => return Err(format!("Not a complex number: {s}")),
+            };
+            let suffix = match &tokens[3].token {
+                TokenType::Ident(w) => match w.as_str() {
+                    "i" => Suffix::I,
+                    "j" => Suffix::J,
+                    _ => return Err(format!("Not a complex number: {s}")),
+                },
+                _ => {
+                    return Err(format!("Not a complex number: {s}"));
+                }
+            };
+            if matches!(operation, OpSum::Minus) {
+                number2 = -number2
+            }
+            Ok((sign * number1, number2, suffix))
+        }
+        _ => Err(format!("Not a complex number: {s}")),
+    }
+}
+
+impl Model {
+    fn get_complex_number(
+        &mut self,
+        node: &Node,
+        cell: CellReference,
+    ) -> Result<(f64, f64, Suffix), CalcResult> {
+        let value = match self.get_string(node, cell) {
+            Ok(s) => s,
+            Err(s) => return Err(s),
+        };
+        if value.is_empty() {
+            return Ok((0.0, 0.0, Suffix::I));
+        }
+        match parse_complex_number(&value) {
+            Ok(s) => Ok(s),
+            Err(message) => Err(CalcResult::new_error(Error::NUM, cell, message)),
+        }
+    }
+    // COMPLEX(real_num, i_num, [suffix])
+    pub(crate) fn fn_complex(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(2..=3).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let x = match self.get_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let y = match self.get_number(&args[1], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let suffix = if args.len() == 3 {
+            match self.get_string(&args[2], cell) {
+                Ok(s) => {
+                    if s == "i" || s.is_empty() {
+                        Suffix::I
+                    } else if s == "j" {
+                        Suffix::J
+                    } else {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Invalid suffix".to_string(),
+                        );
+                    }
+                }
+                Err(s) => return s,
+            }
+        } else {
+            Suffix::I
+        };
+
+        let complex = Complex { x, y, suffix };
+        CalcResult::String(complex.to_string())
+    }
+
+    pub(crate) fn fn_imabs(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, _) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        CalcResult::Number(f64::sqrt(x * x + y * y))
+    }
+
+    pub(crate) fn fn_imaginary(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (_, y, _) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        CalcResult::Number(y)
+    }
+    pub(crate) fn fn_imargument(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, _) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        if x == 0.0 && y == 0.0 {
+            return CalcResult::new_error(Error::DIV, cell, "Division by zero".to_string());
+        }
+        let angle = f64::atan2(y, x);
+        CalcResult::Number(angle)
+    }
+    pub(crate) fn fn_imconjugate(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let complex = Complex { x, y: -y, suffix };
+        CalcResult::String(complex.to_string())
+    }
+    pub(crate) fn fn_imcos(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let (x, y, suffix) = match parse_complex_number(&value) {
+            Ok(s) => s,
+            Err(message) => return CalcResult::new_error(Error::NUM, cell, message),
+        };
+
+        let complex = Complex {
+            x: x.cos() * y.cosh(),
+            y: -x.sin() * y.sinh(),
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+    pub(crate) fn fn_imcosh(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let (x, y, suffix) = match parse_complex_number(&value) {
+            Ok(s) => s,
+            Err(message) => return CalcResult::new_error(Error::NUM, cell, message),
+        };
+
+        let complex = Complex {
+            x: x.cosh() * y.cos(),
+            y: x.sinh() * y.sin(),
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+    pub(crate) fn fn_imcot(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let (x, y, suffix) = match parse_complex_number(&value) {
+            Ok(s) => s,
+            Err(message) => return CalcResult::new_error(Error::NUM, cell, message),
+        };
+
+        if x == 0.0 && y != 0.0 {
+            let complex = Complex {
+                x: 0.0,
+                y: -1.0 / y.tanh(),
+                suffix,
+            };
+            return CalcResult::String(complex.to_string());
+        } else if y == 0.0 {
+            let complex = Complex {
+                x: 1.0 / x.tan(),
+                y: 0.0,
+                suffix,
+            };
+            return CalcResult::String(complex.to_string());
+        }
+
+        let x_cot = 1.0 / x.tan();
+        let y_coth = 1.0 / y.tanh();
+
+        let t = x_cot * x_cot + y_coth * y_coth;
+        let x = (x_cot * y_coth * y_coth - x_cot) / t;
+        let y = (-x_cot * x_cot * y_coth - y_coth) / t;
+
+        if x.is_infinite() || y.is_infinite() || x.is_nan() || y.is_nan() {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid operation".to_string());
+        }
+
+        let complex = Complex { x, y, suffix };
+        CalcResult::String(complex.to_string())
+    }
+
+    pub(crate) fn fn_imcsc(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let (x, y, suffix) = match parse_complex_number(&value) {
+            Ok(s) => s,
+            Err(message) => return CalcResult::new_error(Error::NUM, cell, message),
+        };
+        let x_cos = x.cos();
+        let x_sin = x.sin();
+
+        let y_cosh = y.cosh();
+        let y_sinh = y.sinh();
+
+        let t = x_sin * x_sin * y_cosh * y_cosh + x_cos * x_cos * y_sinh * y_sinh;
+
+        let complex = Complex {
+            x: x_sin * y_cosh / t,
+            y: -x_cos * y_sinh / t,
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+
+    pub(crate) fn fn_imcsch(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let (x, y, suffix) = match parse_complex_number(&value) {
+            Ok(s) => s,
+            Err(message) => return CalcResult::new_error(Error::NUM, cell, message),
+        };
+        let x_cosh = x.cosh();
+        let x_sinh = x.sinh();
+
+        let y_cos = y.cos();
+        let y_sin = y.sin();
+
+        let t = x_sinh * x_sinh * y_cos * y_cos + x_cosh * x_cosh * y_sin * y_sin;
+
+        let complex = Complex {
+            x: x_sinh * y_cos / t,
+            y: -x_cosh * y_sin / t,
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+
+    pub(crate) fn fn_imdiv(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x1, y1, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let (x2, y2, suffix2) = match self.get_complex_number(&args[1], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        if suffix != suffix2 {
+            return CalcResult::new_error(Error::VALUE, cell, "Different suffixes".to_string());
+        }
+        let t = x2 * x2 + y2 * y2;
+        if t == 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid".to_string());
+        }
+        let complex = Complex {
+            x: (x1 * x2 + y1 * y2) / t,
+            y: (-x1 * y2 + y1 * x2) / t,
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+
+    pub(crate) fn fn_imexp(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+
+        let complex = Complex {
+            x: x.exp() * y.cos(),
+            y: x.exp() * y.sin(),
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+    pub(crate) fn fn_imln(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+
+        let r = f64::sqrt(x * x + y * y);
+        let a = f64::atan2(y, x);
+
+        let complex = Complex {
+            x: r.ln(),
+            y: a,
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+    pub(crate) fn fn_imlog10(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+
+        let r = f64::sqrt(x * x + y * y);
+        let a = f64::atan2(y, x);
+
+        let complex = Complex {
+            x: r.log10(),
+            y: a * f64::log10(f64::exp(1.0)),
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+    pub(crate) fn fn_imlog2(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+
+        let r = f64::sqrt(x * x + y * y);
+        let a = f64::atan2(y, x);
+
+        let complex = Complex {
+            x: r.log2(),
+            y: a * f64::log2(f64::exp(1.0)),
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+
+    // IMPOWER(imnumber, power)
+    // If $(r, \theta)$ is the polar representation the formula is:
+    //  $$ x = r^n*\cos(n\dot\theta), y = r^n*\csin(n\dot\theta) $
+    pub(crate) fn fn_impower(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+
+        let (x, y, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+
+        let n = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+
+        // if n == n.trunc() && n < 10.0 {
+        //     // for small powers we compute manually
+        //     let (mut x0, mut y0) = (x, y);
+        //     for _ in 1..(n.trunc() as i32) {
+        //         (x0, y0) = (x0 * x - y0 * y, x0 * y + y0 * x);
+        //     }
+        //     let complex = Complex {
+        //         x: x0,
+        //         y: y0,
+        //         suffix,
+        //     };
+        //     return CalcResult::String(complex.to_string());
+        // };
+
+        let r = f64::sqrt(x * x + y * y);
+        let a = f64::atan2(y, x);
+
+        let x = r.powf(n) * f64::cos(a * n);
+        let y = r.powf(n) * f64::sin(a * n);
+
+        if x.is_infinite() || y.is_infinite() || x.is_nan() || y.is_nan() {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid operation".to_string());
+        }
+
+        let complex = Complex { x, y, suffix };
+        CalcResult::String(complex.to_string())
+    }
+
+    pub(crate) fn fn_improduct(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+
+        let (x1, y1, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+
+        let (x2, y2, suffix2) = match self.get_complex_number(&args[1], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+
+        if suffix != suffix2 {
+            return CalcResult::new_error(Error::VALUE, cell, "Different suffixes".to_string());
+        }
+        let complex = Complex {
+            x: x1 * x2 - y1 * y2,
+            y: x1 * y2 + y1 * x2,
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+    pub(crate) fn fn_imreal(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, _, _) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        CalcResult::Number(x)
+    }
+    pub(crate) fn fn_imsec(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let x_cos = x.cos();
+        let x_sin = x.sin();
+
+        let y_cosh = y.cosh();
+        let y_sinh = y.sinh();
+
+        let t = x_cos * x_cos * y_cosh * y_cosh + x_sin * x_sin * y_sinh * y_sinh;
+
+        let complex = Complex {
+            x: x_cos * y_cosh / t,
+            y: x_sin * y_sinh / t,
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+    pub(crate) fn fn_imsech(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let x_cosh = x.cosh();
+        let x_sinh = x.sinh();
+
+        let y_cos = y.cos();
+        let y_sin = y.sin();
+
+        let t = x_cosh * x_cosh * y_cos * y_cos + x_sinh * x_sinh * y_sin * y_sin;
+
+        let complex = Complex {
+            x: x_cosh * y_cos / t,
+            y: -x_sinh * y_sin / t,
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+    pub(crate) fn fn_imsin(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+
+        let complex = Complex {
+            x: x.sin() * y.cosh(),
+            y: x.cos() * y.sinh(),
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+    pub(crate) fn fn_imsinh(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+
+        let complex = Complex {
+            x: x.sinh() * y.cos(),
+            y: x.cosh() * y.sin(),
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+    pub(crate) fn fn_imsqrt(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+
+        let r = f64::sqrt(x * x + y * y).sqrt();
+        let a = f64::atan2(y, x);
+
+        let complex = Complex {
+            x: r * f64::cos(a / 2.0),
+            y: r * f64::sin(a / 2.0),
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+    pub(crate) fn fn_imsub(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x1, y1, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let (x2, y2, suffix2) = match self.get_complex_number(&args[1], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        if suffix != suffix2 {
+            return CalcResult::new_error(Error::VALUE, cell, "Different suffixes".to_string());
+        }
+        let complex = Complex {
+            x: x1 - x2,
+            y: y1 - y2,
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+
+    pub(crate) fn fn_imsum(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x1, y1, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let (x2, y2, suffix2) = match self.get_complex_number(&args[1], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        if suffix != suffix2 {
+            return CalcResult::new_error(Error::VALUE, cell, "Different suffixes".to_string());
+        }
+        let complex = Complex {
+            x: x1 + x2,
+            y: y1 + y2,
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+
+    pub(crate) fn fn_imtan(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let (x, y, suffix) = match self.get_complex_number(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+
+        let x_tan = x.tan();
+        let y_tanh = y.tanh();
+
+        let t = 1.0 + x_tan * x_tan * y_tanh * y_tanh;
+
+        let complex = Complex {
+            x: (x_tan - x_tan * y_tanh * y_tanh) / t,
+            y: (y_tanh + x_tan * x_tan * y_tanh) / t,
+            suffix,
+        };
+        CalcResult::String(complex.to_string())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::functions::engineering::complex::Suffix;
+
+    use super::parse_complex_number as parse;
+
+    #[test]
+    fn test_parse_complex() {
+        assert_eq!(parse("1+2i"), Ok((1.0, 2.0, Suffix::I)));
+        assert_eq!(parse("2i"), Ok((0.0, 2.0, Suffix::I)));
+        assert_eq!(parse("7.5"), Ok((7.5, 0.0, Suffix::I)));
+        assert_eq!(parse("-7.5"), Ok((-7.5, 0.0, Suffix::I)));
+        assert_eq!(parse("7-5i"), Ok((7.0, -5.0, Suffix::I)));
+        assert_eq!(parse("i"), Ok((0.0, 1.0, Suffix::I)));
+        assert_eq!(parse("7+i"), Ok((7.0, 1.0, Suffix::I)));
+        assert_eq!(parse("7-i"), Ok((7.0, -1.0, Suffix::I)));
+        assert_eq!(parse("-i"), Ok((0.0, -1.0, Suffix::I)));
+        assert_eq!(parse("0"), Ok((0.0, 0.0, Suffix::I)));
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/convert.rs.html b/src/ironcalc_base/functions/engineering/convert.rs.html new file mode 100644 index 0000000..90aa35f --- /dev/null +++ b/src/ironcalc_base/functions/engineering/convert.rs.html @@ -0,0 +1,837 @@ +convert.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+
use std::collections::HashMap;
+
+use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::parser::Node,
+    expressions::token::Error,
+    model::Model,
+};
+
+enum Temperature {
+    Kelvin,
+    Celsius,
+    Rankine,
+    Reaumur,
+    Fahrenheit,
+}
+
+// To Kelvin
+// T_K = T_C + 273.15
+// T_K = 5/9 * T_rank
+// T_K = (T_R-273.15)*4/5
+// T_K = 5/9 ( T_F + 459.67)
+fn convert_temperature(
+    value: f64,
+    from_temperature: Temperature,
+    to_temperature: Temperature,
+) -> f64 {
+    let from_t_kelvin = match from_temperature {
+        Temperature::Kelvin => value,
+        Temperature::Celsius => value + 273.15,
+        Temperature::Rankine => 5.0 * value / 9.0,
+        Temperature::Reaumur => 5.0 * value / 4.0 + 273.15,
+        Temperature::Fahrenheit => 5.0 / 9.0 * (value + 459.67),
+    };
+
+    match to_temperature {
+        Temperature::Kelvin => from_t_kelvin,
+        Temperature::Celsius => from_t_kelvin - 273.5,
+        Temperature::Rankine => 9.0 * from_t_kelvin / 5.0,
+        Temperature::Reaumur => 4.0 * (from_t_kelvin - 273.15) / 5.0,
+        Temperature::Fahrenheit => 9.0 * from_t_kelvin / 5.0 - 459.67,
+    }
+}
+
+impl Model {
+    // CONVERT(number, from_unit, to_unit)
+    pub(crate) fn fn_convert(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let from_unit = match self.get_string(&args[1], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let to_unit = match self.get_string(&args[2], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let prefix = HashMap::from([
+            ("Y", 1E+24),
+            ("Z", 1E+21),
+            ("E", 1000000000000000000.0),
+            ("P", 1000000000000000.0),
+            ("T", 1000000000000.0),
+            ("G", 1000000000.0),
+            ("M", 1000000.0),
+            ("k", 1000.0),
+            ("h", 100.0),
+            ("da", 10.0),
+            ("e", 10.0),
+            ("d", 0.1),
+            ("c", 0.01),
+            ("m", 0.001),
+            ("u", 0.000001),
+            ("n", 0.000000001),
+            ("p", 1E-12),
+            ("f", 1E-15),
+            ("a", 1E-18),
+            ("z", 1E-21),
+            ("y", 1E-24),
+            ("Yi", 2.0_f64.powf(80.0)),
+            ("Ei", 2.0_f64.powf(70.0)),
+            ("Yi", 2.0_f64.powf(80.0)),
+            ("Zi", 2.0_f64.powf(70.0)),
+            ("Ei", 2.0_f64.powf(60.0)),
+            ("Pi", 2.0_f64.powf(50.0)),
+            ("Ti", 2.0_f64.powf(40.0)),
+            ("Gi", 2.0_f64.powf(30.0)),
+            ("Mi", 2.0_f64.powf(20.0)),
+            ("ki", 2.0_f64.powf(10.0)),
+        ]);
+
+        let mut units = HashMap::new();
+
+        let weight = HashMap::from([
+            ("g", 1.0),
+            ("sg", 14593.9029372064),
+            ("lbm", 453.59237),
+            ("u", 1.660538782E-24),
+            ("ozm", 28.349523125),
+            ("grain", 0.06479891),
+            ("cwt", 45359.237),
+            ("shweight", 45359.237),
+            ("uk_cwt", 50802.34544),
+            ("lcwt", 50802.34544),
+            ("stone", 6350.29318),
+            ("ton", 907184.74),
+            ("brton", 1016046.9088), // g-sheets has a different value for this
+            ("LTON", 1016046.9088),
+            ("uk_ton", 1016046.9088),
+        ]);
+
+        units.insert("weight", weight);
+
+        let distance = HashMap::from([
+            ("m", 1.0),
+            ("mi", 1609.344),
+            ("Nmi", 1852.0),
+            ("in", 0.0254),
+            ("ft", 0.3048),
+            ("yd", 0.9144),
+            ("ang", 0.0000000001),
+            ("ell", 1.143),
+            ("ly", 9460730472580800.0),
+            ("parsec", 30856775812815500.0),
+            ("pc", 30856775812815500.0),
+            ("Picapt", 0.000352777777777778),
+            ("Pica", 0.000352777777777778),
+            ("pica", 0.00423333333333333),
+            ("survey_mi", 1609.34721869444),
+        ]);
+        units.insert("distance", distance);
+        let time = HashMap::from([
+            ("yr", 31557600.0),
+            ("day", 86400.0),
+            ("d", 86400.0),
+            ("hr", 3600.0),
+            ("mn", 60.0),
+            ("min", 60.0),
+            ("sec", 1.0),
+            ("s", 1.0),
+        ]);
+
+        units.insert("time", time);
+
+        let pressure = HashMap::from([
+            ("Pa", 1.0),
+            ("p", 1.0),
+            ("atm", 101325.0),
+            ("at", 101325.0),
+            ("mmHg", 133.322),
+            ("psi", 6894.75729316836),
+            ("Torr", 133.322368421053),
+        ]);
+        units.insert("pressure", pressure);
+        let force = HashMap::from([
+            ("N", 1.0),
+            ("dyn", 0.00001),
+            ("dy", 0.00001),
+            ("lbf", 4.4482216152605),
+            ("pond", 0.00980665),
+        ]);
+        units.insert("force", force);
+
+        let energy = HashMap::from([
+            ("J", 1.0),
+            ("e", 0.0000001),
+            ("c", 4.184),
+            ("cal", 4.1868),
+            ("eV", 1.602176487E-19),
+            ("ev", 1.602176487E-19),
+            ("HPh", 2684519.53769617),
+            ("hh", 2684519.53769617),
+            ("Wh", 3600.0),
+            ("wh", 3600.0),
+            ("flb", 1.3558179483314),
+            ("BTU", 1055.05585262),
+            ("btu", 1055.05585262),
+        ]);
+        units.insert("energy", energy);
+
+        let power = HashMap::from([
+            ("HP", 745.69987158227),
+            ("h", 745.69987158227),
+            ("PS", 735.49875),
+            ("W", 1.0),
+            ("w", 1.0),
+        ]);
+        units.insert("power", power);
+
+        let magnetism = HashMap::from([("T", 1.0), ("ga", 0.0001)]);
+        units.insert("magnetism", magnetism);
+
+        let volume = HashMap::from([
+            ("tsp", 0.00000492892159375),
+            ("tspm", 0.000005),
+            ("tbs", 0.00001478676478125),
+            ("oz", 0.0000295735295625),
+            ("cup", 0.0002365882365),
+            ("pt", 0.000473176473),
+            ("us_pt", 0.000473176473),
+            ("uk_pt", 0.00056826125),
+            ("qt", 0.000946352946),
+            ("uk_qt", 0.0011365225),
+            ("gal", 0.003785411784),
+            ("uk_gal", 0.00454609),
+            ("l", 0.001),
+            ("L", 0.001),
+            ("lt", 0.001),
+            ("ang3", 1E-30),
+            ("ang^3", 1E-30),
+            ("barrel", 0.158987294928),
+            ("bushel", 0.03523907016688),
+            ("ft3", 0.028316846592),
+            ("ft^3", 0.028316846592),
+            ("in3", 0.000016387064),
+            ("in^3", 0.000016387064),
+            ("ly3", 8.46786664623715E+47),
+            ("ly^3", 8.46786664623715E+47),
+            ("m3", 1.0),
+            ("m^3", 1.0),
+            ("mi3", 4168181825.44058),
+            ("mi^3", 4168181825.44058),
+            ("yd3", 0.764554857984),
+            ("yd^3", 0.764554857984),
+            ("Nmi3", 6352182208.0),
+            ("Nmi^3", 6352182208.0),
+            ("Picapt3", 4.39039566186557E-11),
+            ("Picapt^3", 4.39039566186557E-11),
+            ("Pica3", 4.39039566186557E-11),
+            ("Pica^3", 4.39039566186557E-11),
+            ("GRT", 2.8316846592),
+            ("regton", 2.8316846592),
+            ("MTON", 1.13267386368),
+        ]);
+        units.insert("volume", volume);
+
+        let area = HashMap::from([
+            ("uk_acre", 4046.8564224),
+            ("us_acre", 4046.87260987425),
+            ("ang2", 1E-20),
+            ("ang^2", 1E-20),
+            ("ar", 100.0),
+            ("ft2", 0.09290304),
+            ("ft^2", 0.09290304),
+            ("ha", 10000.0),
+            ("in2", 0.00064516),
+            ("in^2", 0.00064516),
+            ("ly2", 8.95054210748189E+31),
+            ("ly^2", 8.95054210748189E+31),
+            ("m2", 1.0),
+            ("m^2", 1.0),
+            ("Morgen", 2500.0),
+            ("mi2", 2589988.110336),
+            ("mi^2", 2589988.110336),
+            ("Nmi2", 3429904.0),
+            ("Nmi^2", 3429904.0),
+            ("Picapt2", 0.000000124452160493827),
+            ("Pica2", 0.000000124452160493827),
+            ("Pica^2", 0.000000124452160493827),
+            ("Picapt^2", 0.000000124452160493827),
+            ("yd2", 0.83612736),
+            ("yd^2", 0.83612736),
+        ]);
+        units.insert("area", area);
+
+        let information = HashMap::from([("bit", 1.0), ("byte", 8.0)]);
+        units.insert("information", information);
+
+        let speed = HashMap::from([
+            ("admkn", 0.514773333333333),
+            ("kn", 0.514444444444444),
+            ("m/h", 0.000277777777777778),
+            ("m/hr", 0.000277777777777778),
+            ("m/s", 1.0),
+            ("m/sec", 1.0),
+            ("mph", 0.44704),
+        ]);
+        units.insert("speed", speed);
+
+        let temperature = HashMap::from([
+            ("C", 1.0),
+            ("cel", 1.0),
+            ("F", 1.0),
+            ("fah", 1.0),
+            ("K", 1.0),
+            ("kel", 1.0),
+            ("Rank", 1.0),
+            ("Reau", 1.0),
+        ]);
+        units.insert("temperature", temperature);
+
+        // only some units admit prefixes (the is no kC, kilo Celsius, for instance)
+        let mks = [
+            "Pa", "p", "atm", "at", "mmHg", "g", "u", "m", "ang", "ly", "parsec", "pc", "ang2",
+            "ang^2", "ar", "m2", "m^2", "N", "dyn", "dy", "pond", "J", "e", "c", "cal", "eV", "ev",
+            "Wh", "wh", "W", "w", "T", "ga", "uk_pt", "l", "L", "lt", "ang3", "ang^3", "m3", "m^3",
+            "bit", "byte", "m/h", "m/hr", "m/s", "m/sec", "mph", "K", "kel",
+        ];
+        let volumes = ["ang3", "ang^3", "m3", "m^3"];
+
+        // We need all_units to make sure tha pc is interpreted as parsec and not pico centimeters
+        // We could have this list hard coded, of course.
+        let mut all_units = Vec::new();
+        for unit in units.values() {
+            for &unit_name in unit.keys() {
+                all_units.push(unit_name);
+            }
+        }
+
+        let mut to_unit_prefix = 1.0;
+        let mut from_unit_prefix = 1.0;
+
+        // kind of units (weight, distance, time, ...)
+        let mut to_unit_kind = "";
+        let mut from_unit_kind = "";
+
+        let mut to_unit_name = "";
+        let mut from_unit_name = "";
+
+        for (&name, unit) in &units {
+            for (&unit_name, unit_value) in unit {
+                if let Some(pk) = from_unit.strip_suffix(unit_name) {
+                    if pk.is_empty() {
+                        from_unit_kind = name;
+                        from_unit_prefix = 1.0 * unit_value;
+                        from_unit_name = unit_name;
+                    } else if let Some(modifier) = prefix.get(pk) {
+                        if mks.contains(&unit_name) && !all_units.contains(&from_unit.as_str()) {
+                            // We make sure:
+                            // 1. It is a unit that admits a modifier (like metres or grams)
+                            // 2. from_unit is not itself a unit
+                            let scale = if name == "area" && unit_name != "ar" {
+                                // 1 km2 is actually 10^6 m2
+                                *modifier * modifier
+                            } else if name == "volume" && volumes.contains(&unit_name) {
+                                // don't look at me I don't make the rules!
+                                *modifier * modifier * modifier
+                            } else {
+                                *modifier
+                            };
+                            from_unit_kind = name;
+                            from_unit_prefix = scale * unit_value;
+                            from_unit_name = unit_name;
+                        }
+                    }
+                }
+                if let Some(pk) = to_unit.strip_suffix(unit_name) {
+                    if pk.is_empty() {
+                        to_unit_kind = name;
+                        to_unit_prefix = 1.0 * unit_value;
+                        to_unit_name = unit_name;
+                    } else if let Some(modifier) = prefix.get(pk) {
+                        if mks.contains(&unit_name) && !all_units.contains(&to_unit.as_str()) {
+                            let scale = if name == "area" && unit_name != "ar" {
+                                *modifier * modifier
+                            } else if name == "volume" && volumes.contains(&unit_name) {
+                                *modifier * modifier * modifier
+                            } else {
+                                *modifier
+                            };
+                            to_unit_kind = name;
+                            to_unit_prefix = scale * unit_value;
+                            to_unit_name = unit_name;
+                        }
+                    }
+                }
+                if !from_unit_kind.is_empty() && !to_unit_kind.is_empty() {
+                    break;
+                }
+            }
+            if !from_unit_kind.is_empty() && !to_unit_kind.is_empty() {
+                break;
+            }
+        }
+        if from_unit_kind != to_unit_kind {
+            return CalcResult::new_error(Error::NA, cell, "Different units".to_string());
+        }
+
+        // Let's check if it is temperature;
+        if from_unit_kind.is_empty() {
+            return CalcResult::new_error(Error::NA, cell, "Unit not found".to_string());
+        }
+
+        if from_unit_kind == "temperature" {
+            // Temperature requires formula conversion
+            // Kelvin (K, k), Celsius (C,cel), Rankine (Rank), Réaumur (Reau)
+            let to_temperature = match to_unit_name {
+                "K" | "kel" => Temperature::Kelvin,
+                "C" | "cel" => Temperature::Celsius,
+                "Rank" => Temperature::Rankine,
+                "Reau" => Temperature::Reaumur,
+                "F" | "fah" => Temperature::Fahrenheit,
+                _ => {
+                    return CalcResult::new_error(Error::ERROR, cell, "Internal error".to_string());
+                }
+            };
+            let from_temperature = match from_unit_name {
+                "K" | "kel" => Temperature::Kelvin,
+                "C" | "cel" => Temperature::Celsius,
+                "Rank" => Temperature::Rankine,
+                "Reau" => Temperature::Reaumur,
+                "F" | "fah" => Temperature::Fahrenheit,
+                _ => {
+                    return CalcResult::new_error(Error::ERROR, cell, "Internal error".to_string());
+                }
+            };
+            let t = convert_temperature(value * from_unit_prefix, from_temperature, to_temperature)
+                / to_unit_prefix;
+            return CalcResult::Number(t);
+        }
+        CalcResult::Number(value * from_unit_prefix / to_unit_prefix)
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/misc.rs.html b/src/ironcalc_base/functions/engineering/misc.rs.html new file mode 100644 index 0000000..42e58f8 --- /dev/null +++ b/src/ironcalc_base/functions/engineering/misc.rs.html @@ -0,0 +1,119 @@ +misc.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+
use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::parser::Node,
+    model::Model,
+    number_format::to_precision,
+};
+
+impl Model {
+    // DELTA(number1, [number2])
+    pub(crate) fn fn_delta(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(1..=2).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let number1 = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(error) => return error,
+        };
+        let number2 = if arg_count > 1 {
+            match self.get_number_no_bools(&args[1], cell) {
+                Ok(f) => f,
+                Err(error) => return error,
+            }
+        } else {
+            0.0
+        };
+
+        if to_precision(number1, 16) == to_precision(number2, 16) {
+            CalcResult::Number(1.0)
+        } else {
+            CalcResult::Number(0.0)
+        }
+    }
+
+    // GESTEP(number, [step])
+    pub(crate) fn fn_gestep(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(1..=2).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let number = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(error) => return error,
+        };
+        let step = if arg_count > 1 {
+            match self.get_number_no_bools(&args[1], cell) {
+                Ok(f) => f,
+                Err(error) => return error,
+            }
+        } else {
+            0.0
+        };
+        if to_precision(number, 16) >= to_precision(step, 16) {
+            CalcResult::Number(1.0)
+        } else {
+            CalcResult::Number(0.0)
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/mod.rs.html b/src/ironcalc_base/functions/engineering/mod.rs.html new file mode 100644 index 0000000..2443fd2 --- /dev/null +++ b/src/ironcalc_base/functions/engineering/mod.rs.html @@ -0,0 +1,15 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+
mod bessel;
+mod bit_operations;
+mod complex;
+mod convert;
+mod misc;
+mod number_basis;
+mod transcendental;
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/number_basis.rs.html b/src/ironcalc_base/functions/engineering/number_basis.rs.html new file mode 100644 index 0000000..ef9d823 --- /dev/null +++ b/src/ironcalc_base/functions/engineering/number_basis.rs.html @@ -0,0 +1,1093 @@ +number_basis.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+
use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::parser::Node,
+    expressions::token::Error,
+    model::Model,
+};
+
+// 8_i64.pow(10);
+const OCT_MAX: i64 = 1_073_741_824;
+const OCT_MAX_HALF: i64 = 536_870_912;
+// 16_i64.pow(10)
+const HEX_MAX: i64 = 1_099_511_627_776;
+const HEX_MAX_HALF: i64 = 549_755_813_888;
+// Binary numbers are 10 bits and the most significant bit is the sign
+
+fn from_binary_to_decimal(value: f64) -> Result<i64, String> {
+    let value = format!("{value}");
+
+    let result = match i64::from_str_radix(&value, 2) {
+        Ok(b) => b,
+        Err(_) => {
+            return Err("cannot parse into binary".to_string());
+        }
+    };
+    if !(0..=1023).contains(&result) {
+        // 2^10
+        return Err("too large".to_string());
+    } else if result > 511 {
+        // 2^9
+        return Ok(result - 1024);
+    };
+    Ok(result)
+}
+
+impl Model {
+    // BIN2DEC(number)
+    pub(crate) fn fn_bin2dec(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        match from_binary_to_decimal(value) {
+            Ok(n) => CalcResult::Number(n as f64),
+            Err(message) => CalcResult::new_error(Error::NUM, cell, message),
+        }
+    }
+
+    // BIN2HEX(number, [places])
+    pub(crate) fn fn_bin2hex(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(1..=2).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let places = if args.len() == 2 {
+            match self.get_number_no_bools(&args[1], cell) {
+                Ok(f) => Some(f.trunc() as i32),
+                Err(s) => return s,
+            }
+        } else {
+            None
+        };
+        if let Some(p) = places {
+            if p <= 0 || p > 10 {
+                return CalcResult::new_error(Error::NUM, cell, "Not enough places".to_string());
+            }
+        }
+        let value = match from_binary_to_decimal(value) {
+            Ok(n) => n,
+            Err(message) => return CalcResult::new_error(Error::NUM, cell, message),
+        };
+        if value < 0 {
+            CalcResult::String(format!("{:0width$X}", HEX_MAX + value, width = 9))
+        } else {
+            let result = format!("{:X}", value);
+            if let Some(places) = places {
+                if places < result.len() as i32 {
+                    return CalcResult::new_error(
+                        Error::NUM,
+                        cell,
+                        "Not enough places".to_string(),
+                    );
+                }
+                return CalcResult::String(format!("{:0width$X}", value, width = places as usize));
+            }
+            CalcResult::String(result)
+        }
+    }
+
+    // BIN2OCT(number, [places])
+    pub(crate) fn fn_bin2oct(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(1..=2).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let places = if args.len() == 2 {
+            match self.get_number_no_bools(&args[1], cell) {
+                Ok(f) => Some(f.trunc() as i32),
+                Err(s) => return s,
+            }
+        } else {
+            None
+        };
+        let value = match from_binary_to_decimal(value) {
+            Ok(n) => n,
+            Err(message) => return CalcResult::new_error(Error::NUM, cell, message),
+        };
+        if let Some(p) = places {
+            if p <= 0 || p > 10 {
+                return CalcResult::new_error(Error::NUM, cell, "Not enough places".to_string());
+            }
+        }
+        if value < 0 {
+            CalcResult::String(format!("{:0width$o}", OCT_MAX + value, width = 9))
+        } else {
+            let result = format!("{:o}", value);
+            if let Some(places) = places {
+                if places < result.len() as i32 {
+                    return CalcResult::new_error(
+                        Error::NUM,
+                        cell,
+                        "Not enough places".to_string(),
+                    );
+                }
+                return CalcResult::String(format!("{:0width$o}", value, width = places as usize));
+            }
+            CalcResult::String(result)
+        }
+    }
+
+    pub(crate) fn fn_dec2bin(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(1..=2).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value_raw = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let places = if args.len() == 2 {
+            match self.get_number_no_bools(&args[1], cell) {
+                Ok(f) => Some(f.trunc() as i32),
+                Err(s) => return s,
+            }
+        } else {
+            None
+        };
+        if let Some(p) = places {
+            if p <= 0 || p > 10 {
+                return CalcResult::new_error(Error::NUM, cell, "Not enough places".to_string());
+            }
+        }
+        let mut value = value_raw.trunc() as i64;
+        if !(-512..=511).contains(&value) {
+            return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+        }
+        if value < 0 {
+            value += 1024;
+        }
+        let result = format!("{:b}", value);
+        if let Some(places) = places {
+            if value_raw > 0.0 && places < result.len() as i32 {
+                return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+            }
+            let result = format!("{:0width$b}", value, width = places as usize);
+            return CalcResult::String(result);
+        }
+        CalcResult::String(result)
+    }
+
+    pub(crate) fn fn_dec2hex(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(1..=2).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value_raw = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f.trunc(),
+            Err(s) => return s,
+        };
+        let places = if args.len() == 2 {
+            match self.get_number_no_bools(&args[1], cell) {
+                Ok(f) => Some(f.trunc() as i32),
+                Err(s) => return s,
+            }
+        } else {
+            None
+        };
+        if let Some(p) = places {
+            if p <= 0 || p > 10 {
+                return CalcResult::new_error(Error::NUM, cell, "Not enough places".to_string());
+            }
+        }
+        let mut value = value_raw.trunc() as i64;
+        if !(-HEX_MAX_HALF..=HEX_MAX_HALF - 1).contains(&value) {
+            return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+        }
+        if value < 0 {
+            value += HEX_MAX;
+        }
+        let result = format!("{:X}", value);
+        if let Some(places) = places {
+            if value_raw > 0.0 && places < result.len() as i32 {
+                return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+            }
+            let result = format!("{:0width$X}", value, width = places as usize);
+            return CalcResult::String(result);
+        }
+        CalcResult::String(result)
+    }
+
+    pub(crate) fn fn_dec2oct(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(1..=2).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value_raw = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let places = if args.len() == 2 {
+            match self.get_number_no_bools(&args[1], cell) {
+                Ok(f) => Some(f.trunc() as i32),
+                Err(s) => return s,
+            }
+        } else {
+            None
+        };
+        if let Some(p) = places {
+            if p <= 0 || p > 10 {
+                return CalcResult::new_error(Error::NUM, cell, "Not enough places".to_string());
+            }
+        }
+        let mut value = value_raw.trunc() as i64;
+
+        if !(-OCT_MAX_HALF..=OCT_MAX_HALF - 1).contains(&value) {
+            return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+        }
+        if value < 0 {
+            value += OCT_MAX;
+        }
+        let result = format!("{:o}", value);
+        if let Some(places) = places {
+            if value_raw > 0.0 && places < result.len() as i32 {
+                return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+            }
+            let result = format!("{:0width$o}", value, width = places as usize);
+            return CalcResult::String(result);
+        }
+        CalcResult::String(result)
+    }
+
+    // HEX2BIN(number, [places])
+    pub(crate) fn fn_hex2bin(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(1..=2).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let places = if args.len() == 2 {
+            match self.get_number_no_bools(&args[1], cell) {
+                Ok(f) => Some(f.trunc() as i32),
+                Err(s) => return s,
+            }
+        } else {
+            None
+        };
+        if value.len() > 10 {
+            return CalcResult::new_error(Error::NUM, cell, "Value too large".to_string());
+        }
+        if let Some(p) = places {
+            if p <= 0 || p > 10 {
+                return CalcResult::new_error(Error::NUM, cell, "Not enough places".to_string());
+            }
+        }
+        let mut value = match i64::from_str_radix(&value, 16) {
+            Ok(f) => f,
+            Err(_) => {
+                return CalcResult::new_error(
+                    Error::NUM,
+                    cell,
+                    "Error parsing hex number".to_string(),
+                );
+            }
+        };
+        if value < 0 {
+            return CalcResult::new_error(Error::NUM, cell, "Negative value".to_string());
+        }
+
+        if value >= HEX_MAX_HALF {
+            value -= HEX_MAX;
+        }
+        if !(-512..=511).contains(&value) {
+            return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+        }
+        if value < 0 {
+            value += 1024;
+        }
+        let result = format!("{:b}", value);
+        if let Some(places) = places {
+            if places <= 0 || (value > 0 && places < result.len() as i32) {
+                return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+            }
+            let result = format!("{:0width$b}", value, width = places as usize);
+            return CalcResult::String(result);
+        }
+        CalcResult::String(result)
+    }
+
+    // HEX2DEC(number)
+    pub(crate) fn fn_hex2dec(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(1..=2).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        if value.len() > 10 {
+            return CalcResult::new_error(Error::NUM, cell, "Value too large".to_string());
+        }
+        let mut value = match i64::from_str_radix(&value, 16) {
+            Ok(f) => f,
+            Err(_) => {
+                return CalcResult::new_error(
+                    Error::NUM,
+                    cell,
+                    "Error parsing hex number".to_string(),
+                );
+            }
+        };
+        if value < 0 {
+            return CalcResult::new_error(Error::NUM, cell, "Negative value".to_string());
+        }
+
+        if value >= HEX_MAX_HALF {
+            value -= HEX_MAX;
+        }
+        CalcResult::Number(value as f64)
+    }
+
+    pub(crate) fn fn_hex2oct(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(1..=2).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let places = if args.len() == 2 {
+            match self.get_number_no_bools(&args[1], cell) {
+                Ok(f) => Some(f.trunc() as i32),
+                Err(s) => return s,
+            }
+        } else {
+            None
+        };
+        if let Some(p) = places {
+            if p <= 0 || p > 10 {
+                return CalcResult::new_error(Error::NUM, cell, "Not enough places".to_string());
+            }
+        }
+        if value.len() > 10 {
+            return CalcResult::new_error(Error::NUM, cell, "Value too large".to_string());
+        }
+        let mut value = match i64::from_str_radix(&value, 16) {
+            Ok(f) => f,
+            Err(_) => {
+                return CalcResult::new_error(
+                    Error::NUM,
+                    cell,
+                    "Error parsing hex number".to_string(),
+                );
+            }
+        };
+        if value < 0 {
+            return CalcResult::new_error(Error::NUM, cell, "Negative value".to_string());
+        }
+
+        if value > HEX_MAX_HALF {
+            value -= HEX_MAX;
+        }
+        if !(-OCT_MAX_HALF..=OCT_MAX_HALF - 1).contains(&value) {
+            return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+        }
+        if value < 0 {
+            value += OCT_MAX;
+        }
+        let result = format!("{:o}", value);
+        if let Some(places) = places {
+            if places <= 0 || (value > 0 && places < result.len() as i32) {
+                return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+            }
+            let result = format!("{:0width$o}", value, width = places as usize);
+            return CalcResult::String(result);
+        }
+        CalcResult::String(result)
+    }
+
+    pub(crate) fn fn_oct2bin(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(1..=2).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let places = if args.len() == 2 {
+            match self.get_number_no_bools(&args[1], cell) {
+                Ok(f) => Some(f.trunc() as i32),
+                Err(s) => return s,
+            }
+        } else {
+            None
+        };
+        if let Some(p) = places {
+            if p <= 0 || p > 10 {
+                return CalcResult::new_error(Error::NUM, cell, "Not enough places".to_string());
+            }
+        }
+        let mut value = match i64::from_str_radix(&value, 8) {
+            Ok(f) => f,
+            Err(_) => {
+                return CalcResult::new_error(
+                    Error::NUM,
+                    cell,
+                    "Error parsing hex number".to_string(),
+                );
+            }
+        };
+        if value < 0 {
+            return CalcResult::new_error(Error::NUM, cell, "Negative value".to_string());
+        }
+
+        if value >= OCT_MAX_HALF {
+            value -= OCT_MAX;
+        }
+        if !(-512..=511).contains(&value) {
+            return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+        }
+        if value < 0 {
+            value += 1024;
+        }
+        let result = format!("{:b}", value);
+        if let Some(places) = places {
+            if value < 512 && places < result.len() as i32 {
+                return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+            }
+            let result = format!("{:0width$b}", value, width = places as usize);
+            return CalcResult::String(result);
+        }
+        CalcResult::String(result)
+    }
+
+    pub(crate) fn fn_oct2dec(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(1..=2).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let mut value = match i64::from_str_radix(&value, 8) {
+            Ok(f) => f,
+            Err(_) => {
+                return CalcResult::new_error(
+                    Error::NUM,
+                    cell,
+                    "Error parsing hex number".to_string(),
+                );
+            }
+        };
+        if value < 0 {
+            return CalcResult::new_error(Error::NUM, cell, "Negative value".to_string());
+        }
+
+        if value >= OCT_MAX_HALF {
+            value -= OCT_MAX
+        }
+        CalcResult::Number(value as f64)
+    }
+
+    pub(crate) fn fn_oct2hex(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !(1..=2).contains(&args.len()) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let places = if args.len() == 2 {
+            match self.get_number_no_bools(&args[1], cell) {
+                Ok(f) => Some(f.trunc() as i32),
+                Err(s) => return s,
+            }
+        } else {
+            None
+        };
+        // There is not a default value for places
+        // But if there is a value it needs to be positive and less than 11
+        if let Some(p) = places {
+            if p <= 0 || p > 10 {
+                return CalcResult::new_error(Error::NUM, cell, "Not enough places".to_string());
+            }
+        }
+        let mut value = match i64::from_str_radix(&value, 8) {
+            Ok(f) => f,
+            Err(_) => {
+                return CalcResult::new_error(
+                    Error::NUM,
+                    cell,
+                    "Error parsing hex number".to_string(),
+                );
+            }
+        };
+        if value < 0 {
+            return CalcResult::new_error(Error::NUM, cell, "Negative value".to_string());
+        }
+
+        if value >= OCT_MAX_HALF {
+            value -= OCT_MAX;
+        }
+
+        if !(-HEX_MAX_HALF..=HEX_MAX_HALF - 1).contains(&value) {
+            return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+        }
+        if value < 0 {
+            value += HEX_MAX;
+        }
+        let result = format!("{:X}", value);
+        if let Some(places) = places {
+            if value < HEX_MAX_HALF && places < result.len() as i32 {
+                return CalcResult::new_error(Error::NUM, cell, "Out of bounds".to_string());
+            }
+            let result = format!("{:0width$X}", value, width = places as usize);
+            return CalcResult::String(result);
+        }
+        CalcResult::String(result)
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/transcendental/bessel_i.rs.html b/src/ironcalc_base/functions/engineering/transcendental/bessel_i.rs.html new file mode 100644 index 0000000..b638857 --- /dev/null +++ b/src/ironcalc_base/functions/engineering/transcendental/bessel_i.rs.html @@ -0,0 +1,289 @@ +bessel_i.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+
// This are somewhat lower precision than the BesselJ and BesselY
+
+// needed for BesselK
+pub(crate) fn bessel_i0(x: f64) -> f64 {
+    // Parameters of the polynomial approximation
+    let p1 = 1.0;
+    let p2 = 3.5156229;
+    let p3 = 3.0899424;
+    let p4 = 1.2067492;
+    let p5 = 0.2659732;
+    let p6 = 3.60768e-2;
+    let p7 = 4.5813e-3;
+
+    let q1 = 0.39894228;
+    let q2 = 1.328592e-2;
+    let q3 = 2.25319e-3;
+    let q4 = -1.57565e-3;
+    let q5 = 9.16281e-3;
+    let q6 = -2.057706e-2;
+    let q7 = 2.635537e-2;
+    let q8 = -1.647633e-2;
+    let q9 = 3.92377e-3;
+
+    let k1 = 3.75;
+    let ax = x.abs();
+
+    if x.is_infinite() {
+        return 0.0;
+    }
+
+    if ax < k1 {
+        // let xx = x / k1;
+        // let y = xx * xx;
+        // let mut result = 1.0;
+        // let max_iter = 50;
+        // let mut term = 1.0;
+        // for i in 1..max_iter {
+        //     term = term * k1*k1*y /(2.0*i as f64).powi(2);
+        //     result += term;
+        // };
+        // result
+
+        let xx = x / k1;
+        let y = xx * xx;
+        p1 + y * (p2 + y * (p3 + y * (p4 + y * (p5 + y * (p6 + y * p7)))))
+    } else {
+        let y = k1 / ax;
+        ((ax).exp() / (ax).sqrt())
+            * (q1
+                + y * (q2
+                    + y * (q3 + y * (q4 + y * (q5 + y * (q6 + y * (q7 + y * (q8 + y * q9))))))))
+    }
+}
+
+// needed for BesselK
+pub(crate) fn bessel_i1(x: f64) -> f64 {
+    let p1 = 0.5;
+    let p2 = 0.87890594;
+    let p3 = 0.51498869;
+    let p4 = 0.15084934;
+    let p5 = 2.658733e-2;
+    let p6 = 3.01532e-3;
+    let p7 = 3.2411e-4;
+
+    let q1 = 0.39894228;
+    let q2 = -3.988024e-2;
+    let q3 = -3.62018e-3;
+    let q4 = 1.63801e-3;
+    let q5 = -1.031555e-2;
+    let q6 = 2.282967e-2;
+    let q7 = -2.895312e-2;
+    let q8 = 1.787654e-2;
+    let q9 = -4.20059e-3;
+
+    let k1 = 3.75;
+    let ax = x.abs();
+
+    if ax < k1 {
+        let xx = x / k1;
+        let y = xx * xx;
+        x * (p1 + y * (p2 + y * (p3 + y * (p4 + y * (p5 + y * (p6 + y * p7))))))
+    } else {
+        let y = k1 / ax;
+        let result = ((ax).exp() / (ax).sqrt())
+            * (q1
+                + y * (q2
+                    + y * (q3 + y * (q4 + y * (q5 + y * (q6 + y * (q7 + y * (q8 + y * q9))))))));
+        if x < 0.0 {
+            return -result;
+        }
+        result
+    }
+}
+
+pub(crate) fn bessel_i(n: i32, x: f64) -> f64 {
+    let accuracy = 40;
+    let large_number = 1e10;
+    let small_number = 1e-10;
+
+    if n < 0 {
+        return f64::NAN;
+    }
+
+    if n == 0 {
+        return bessel_i0(x);
+    }
+    if x == 0.0 {
+        // I_n(0) = 0 for all n!= 0
+        return 0.0;
+    }
+    if n == 1 {
+        return bessel_i1(x);
+    }
+
+    if x.abs() > large_number {
+        return 0.0;
+    };
+
+    let tox = 2.0 / x.abs();
+    let mut bip = 0.0;
+    let mut bi = 1.0;
+    let mut result = 0.0;
+    let m = 2 * (((accuracy * n) as f64).sqrt().trunc() as i32 + n);
+
+    for j in (1..=m).rev() {
+        (bip, bi) = (bi, bip + (j as f64) * tox * bi);
+        // Prevent overflow
+        if bi.abs() > large_number {
+            result *= small_number;
+            bi *= small_number;
+            bip *= small_number;
+        }
+        if j == n {
+            result = bip;
+        }
+    }
+
+    result *= bessel_i0(x) / bi;
+    if (x < 0.0) && (n % 2 == 1) {
+        result = -result;
+    }
+
+    result
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/transcendental/bessel_j0_y0.rs.html b/src/ironcalc_base/functions/engineering/transcendental/bessel_j0_y0.rs.html new file mode 100644 index 0000000..6e0bafd --- /dev/null +++ b/src/ironcalc_base/functions/engineering/transcendental/bessel_j0_y0.rs.html @@ -0,0 +1,805 @@ +bessel_j0_y0.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+
/* @(#)e_j0.c 1.3 95/01/18 */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+/* j0(x), y0(x)
+ * Bessel function of the first and second kinds of order zero.
+ * Method -- j0(x):
+ *	1. For tiny x, we use j0(x) = 1 - x^2/4 + x^4/64 - ...
+ *	2. Reduce x to |x| since j0(x)=j0(-x),  and
+ *	   for x in (0,2)
+ *		j0(x) = 1-z/4+ z^2*R0/S0,  where z = x*x;
+ *	   (precision:  |j0-1+z/4-z^2R0/S0 |<2**-63.67 )
+ *	   for x in (2,inf)
+ * 		j0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)-q0(x)*sin(x0))
+ * 	   where x0 = x-pi/4. It is better to compute sin(x0),cos(x0)
+ *	   as follow:
+ *		cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4)
+ *			= 1/sqrt(2) * (cos(x) + sin(x))
+ *		sin(x0) = sin(x)cos(pi/4)-cos(x)sin(pi/4)
+ *			= 1/sqrt(2) * (sin(x) - cos(x))
+ * 	   (To avoid cancellation, use
+ *		sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x))
+ * 	    to compute the worse 1.)
+ *
+ *	3 Special cases
+ *		j0(nan)= nan
+ *		j0(0) = 1
+ *		j0(inf) = 0
+ *
+ * Method -- y0(x):
+ *	1. For x<2.
+ *	   Since
+ *		y0(x) = 2/pi*(j0(x)*(ln(x/2)+Euler) + x^2/4 - ...)
+ *	   therefore y0(x)-2/pi*j0(x)*ln(x) is an even function.
+ *	   We use the following function to approximate y0,
+ *		y0(x) = U(z)/V(z) + (2/pi)*(j0(x)*ln(x)), z= x^2
+ *	   where
+ *		U(z) = u00 + u01*z + ... + u06*z^6
+ *		V(z) = 1  + v01*z + ... + v04*z^4
+ *	   with absolute approximation error bounded by 2**-72.
+ *	   Note: For tiny x, U/V = u0 and j0(x)~1, hence
+ *		y0(tiny) = u0 + (2/pi)*ln(tiny), (choose tiny<2**-27)
+ *	2. For x>=2.
+ * 		y0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)+q0(x)*sin(x0))
+ * 	   where x0 = x-pi/4. It is better to compute sin(x0),cos(x0)
+ *	   by the method menti1d above.
+ *	3. Special cases: y0(0)=-inf, y0(x<0)=NaN, y0(inf)=0.
+ */
+
+use std::f64::consts::FRAC_2_PI;
+
+use super::bessel_util::{high_word, split_words, FRAC_2_SQRT_PI, HUGE};
+
+// R0/S0 on [0, 2.00]
+const R02: f64 = 1.562_499_999_999_999_5e-2; // 0x3F8FFFFF, 0xFFFFFFFD
+const R03: f64 = -1.899_792_942_388_547_2e-4; // 0xBF28E6A5, 0xB61AC6E9
+const R04: f64 = 1.829_540_495_327_006_7e-6; // 0x3EBEB1D1, 0x0C503919
+const R05: f64 = -4.618_326_885_321_032e-9; // 0xBE33D5E7, 0x73D63FCE
+const S01: f64 = 1.561_910_294_648_900_1e-2; // 0x3F8FFCE8, 0x82C8C2A4
+const S02: f64 = 1.169_267_846_633_374_5e-4; // 0x3F1EA6D2, 0xDD57DBF4
+const S03: f64 = 5.135_465_502_073_181e-7; // 0x3EA13B54, 0xCE84D5A9
+const S04: f64 = 1.166_140_033_337_9e-9; // 0x3E1408BC, 0xF4745D8F
+
+/* The asymptotic expansions of pzero is
+ *	1 - 9/128 s^2 + 11025/98304 s^4 - ...,	where s = 1/x.
+ * For x >= 2, We approximate pzero by
+ * 	pzero(x) = 1 + (R/S)
+ * where  R = pR0 + pR1*s^2 + pR2*s^4 + ... + pR5*s^10
+ * 	  S = 1 + pS0*s^2 + ... + pS4*s^10
+ * and
+ *	| pzero(x)-1-R/S | <= 2  ** ( -60.26)
+ */
+const P_R8: [f64; 6] = [
+    /* for x in [inf, 8]=1/[0,0.125] */
+    0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */
+    -7.031_249_999_999_004e-2,  /* 0xBFB1FFFF, 0xFFFFFD32 */
+    -8.081_670_412_753_498,     /* 0xC02029D0, 0xB44FA779 */
+    -2.570_631_056_797_048_5e2, /* 0xC0701102, 0x7B19E863 */
+    -2.485_216_410_094_288e3,   /* 0xC0A36A6E, 0xCD4DCAFC */
+    -5.253_043_804_907_295e3,   /* 0xC0B4850B, 0x36CC643D */
+];
+const P_S8: [f64; 5] = [
+    1.165_343_646_196_681_8e2, /* 0x405D2233, 0x07A96751 */
+    3.833_744_753_641_218_3e3, /* 0x40ADF37D, 0x50596938 */
+    4.059_785_726_484_725_5e4, /* 0x40E3D2BB, 0x6EB6B05F */
+    1.167_529_725_643_759_2e5, /* 0x40FC810F, 0x8F9FA9BD */
+    4.762_772_841_467_309_6e4, /* 0x40E74177, 0x4F2C49DC */
+];
+
+const P_R5: [f64; 6] = [
+    /* for x in [8,4.5454]=1/[0.125,0.22001] */
+    -1.141_254_646_918_945e-11, /* 0xBDA918B1, 0x47E495CC */
+    -7.031_249_408_735_993e-2,  /* 0xBFB1FFFF, 0xE69AFBC6 */
+    -4.159_610_644_705_878,     /* 0xC010A370, 0xF90C6BBF */
+    -6.767_476_522_651_673e1,   /* 0xC050EB2F, 0x5A7D1783 */
+    -3.312_312_996_491_729_7e2, /* 0xC074B3B3, 0x6742CC63 */
+    -3.464_333_883_656_049e2,   /* 0xC075A6EF, 0x28A38BD7 */
+];
+const P_S5: [f64; 5] = [
+    6.075_393_826_923_003_4e1, /* 0x404E6081, 0x0C98C5DE */
+    1.051_252_305_957_045_8e3, /* 0x40906D02, 0x5C7E2864 */
+    5.978_970_943_338_558e3,   /* 0x40B75AF8, 0x8FBE1D60 */
+    9.625_445_143_577_745e3,   /* 0x40C2CCB8, 0xFA76FA38 */
+    2.406_058_159_229_391e3,   /* 0x40A2CC1D, 0xC70BE864 */
+];
+
+const P_R3: [f64; 6] = [
+    /* for x in [4.547,2.8571]=1/[0.2199,0.35001] */
+    -2.547_046_017_719_519e-9, /* 0xBE25E103, 0x6FE1AA86 */
+    -7.031_196_163_814_817e-2, /* 0xBFB1FFF6, 0xF7C0E24B */
+    -2.409_032_215_495_296,    /* 0xC00345B2, 0xAEA48074 */
+    -2.196_597_747_348_831e1,  /* 0xC035F74A, 0x4CB94E14 */
+    -5.807_917_047_017_376e1,  /* 0xC04D0A22, 0x420A1A45 */
+    -3.144_794_705_948_885e1,  /* 0xC03F72AC, 0xA892D80F */
+];
+const P_S3: [f64; 5] = [
+    3.585_603_380_552_097e1,   /* 0x4041ED92, 0x84077DD3 */
+    3.615_139_830_503_038_6e2, /* 0x40769839, 0x464A7C0E */
+    1.193_607_837_921_115_3e3, /* 0x4092A66E, 0x6D1061D6 */
+    1.127_996_798_569_074_1e3, /* 0x40919FFC, 0xB8C39B7E */
+    1.735_809_308_133_357_5e2, /* 0x4065B296, 0xFC379081 */
+];
+
+const P_R2: [f64; 6] = [
+    /* for x in [2.8570,2]=1/[0.3499,0.5] */
+    -8.875_343_330_325_264e-8,  /* 0xBE77D316, 0xE927026D */
+    -7.030_309_954_836_247e-2,  /* 0xBFB1FF62, 0x495E1E42 */
+    -1.450_738_467_809_529_9,   /* 0xBFF73639, 0x8A24A843 */
+    -7.635_696_138_235_278,     /* 0xC01E8AF3, 0xEDAFA7F3 */
+    -1.119_316_688_603_567_5e1, /* 0xC02662E6, 0xC5246303 */
+    -3.233_645_793_513_353_4,   /* 0xC009DE81, 0xAF8FE70F */
+];
+const P_S2: [f64; 5] = [
+    2.222_029_975_320_888e1,   /* 0x40363865, 0x908B5959 */
+    1.362_067_942_182_152e2,   /* 0x4061069E, 0x0EE8878F */
+    2.704_702_786_580_835e2,   /* 0x4070E786, 0x42EA079B */
+    1.538_753_942_083_203_3e2, /* 0x40633C03, 0x3AB6FAFF */
+    1.465_761_769_482_562e1,   /* 0x402D50B3, 0x44391809 */
+];
+
+// Note: This function is only called for ix>=0x40000000 (see above)
+fn pzero(x: f64) -> f64 {
+    let ix = high_word(x) & 0x7fffffff;
+    // ix>=0x40000000 && ix<=0x48000000);
+    let (p, q) = if ix >= 0x40200000 {
+        (P_R8, P_S8)
+    } else if ix >= 0x40122E8B {
+        (P_R5, P_S5)
+    } else if ix >= 0x4006DB6D {
+        (P_R3, P_S3)
+    } else {
+        (P_R2, P_S2)
+    };
+    let z = 1.0 / (x * x);
+    let r = p[0] + z * (p[1] + z * (p[2] + z * (p[3] + z * (p[4] + z * p[5]))));
+    let s = 1.0 + z * (q[0] + z * (q[1] + z * (q[2] + z * (q[3] + z * q[4]))));
+    1.0 + r / s
+}
+
+/* For x >= 8, the asymptotic expansions of qzero is
+ *	-1/8 s + 75/1024 s^3 - ..., where s = 1/x.
+ * We approximate pzero by
+ * 	qzero(x) = s*(-1.25 + (R/S))
+ * where  R = qR0 + qR1*s^2 + qR2*s^4 + ... + qR5*s^10
+ * 	  S = 1 + qS0*s^2 + ... + qS5*s^12
+ * and
+ *	| qzero(x)/s +1.25-R/S | <= 2  ** ( -61.22)
+ */
+const Q_R8: [f64; 6] = [
+    /* for x in [inf, 8]=1/[0,0.125] */
+    0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */
+    7.324_218_749_999_35e-2,    /* 0x3FB2BFFF, 0xFFFFFE2C */
+    1.176_820_646_822_527e1,    /* 0x40278952, 0x5BB334D6 */
+    5.576_733_802_564_019e2,    /* 0x40816D63, 0x15301825 */
+    8.859_197_207_564_686e3,    /* 0x40C14D99, 0x3E18F46D */
+    3.701_462_677_768_878e4,    /* 0x40E212D4, 0x0E901566 */
+];
+const Q_S8: [f64; 6] = [
+    1.637_760_268_956_898_2e2, /* 0x406478D5, 0x365B39BC */
+    8.098_344_946_564_498e3,   /* 0x40BFA258, 0x4E6B0563 */
+    1.425_382_914_191_204_8e5, /* 0x41016652, 0x54D38C3F */
+    8.033_092_571_195_144e5,   /* 0x412883DA, 0x83A52B43 */
+    8.405_015_798_190_605e5,   /* 0x4129A66B, 0x28DE0B3D */
+    -3.438_992_935_378_666e5,  /* 0xC114FD6D, 0x2C9530C5 */
+];
+
+const Q_R5: [f64; 6] = [
+    /* for x in [8,4.5454]=1/[0.125,0.22001] */
+    1.840_859_635_945_155_3e-11, /* 0x3DB43D8F, 0x29CC8CD9 */
+    7.324_217_666_126_848e-2,    /* 0x3FB2BFFF, 0xD172B04C */
+    5.835_635_089_620_569_5,     /* 0x401757B0, 0xB9953DD3 */
+    1.351_115_772_864_498_3e2,   /* 0x4060E392, 0x0A8788E9 */
+    1.027_243_765_961_641e3,     /* 0x40900CF9, 0x9DC8C481 */
+    1.989_977_858_646_053_8e3,   /* 0x409F17E9, 0x53C6E3A6 */
+];
+const Q_S5: [f64; 6] = [
+    8.277_661_022_365_378e1,  /* 0x4054B1B3, 0xFB5E1543 */
+    2.077_814_164_213_93e3,   /* 0x40A03BA0, 0xDA21C0CE */
+    1.884_728_877_857_181e4,  /* 0x40D267D2, 0x7B591E6D */
+    5.675_111_228_949_473e4,  /* 0x40EBB5E3, 0x97E02372 */
+    3.597_675_384_251_145e4,  /* 0x40E19118, 0x1F7A54A0 */
+    -5.354_342_756_019_448e3, /* 0xC0B4EA57, 0xBEDBC609 */
+];
+
+const Q_R3: [f64; 6] = [
+    /* for x in [4.547,2.8571]=1/[0.2199,0.35001] */
+    4.377_410_140_897_386e-9,  /* 0x3E32CD03, 0x6ADECB82 */
+    7.324_111_800_429_114e-2,  /* 0x3FB2BFEE, 0x0E8D0842 */
+    3.344_231_375_161_707,     /* 0x400AC0FC, 0x61149CF5 */
+    4.262_184_407_454_126_5e1, /* 0x40454F98, 0x962DAEDD */
+    1.708_080_913_405_656e2,   /* 0x406559DB, 0xE25EFD1F */
+    1.667_339_486_966_511_7e2, /* 0x4064D77C, 0x81FA21E0 */
+];
+const Q_S3: [f64; 6] = [
+    4.875_887_297_245_872e1,   /* 0x40486122, 0xBFE343A6 */
+    7.096_892_210_566_06e2,    /* 0x40862D83, 0x86544EB3 */
+    3.704_148_226_201_113_6e3, /* 0x40ACF04B, 0xE44DFC63 */
+    6.460_425_167_525_689e3,   /* 0x40B93C6C, 0xD7C76A28 */
+    2.516_333_689_203_689_6e3, /* 0x40A3A8AA, 0xD94FB1C0 */
+    -1.492_474_518_361_564e2,  /* 0xC062A7EB, 0x201CF40F */
+];
+
+const Q_R2: [f64; 6] = [
+    /* for x in [2.8570,2]=1/[0.3499,0.5] */
+    1.504_444_448_869_832_7e-7, /* 0x3E84313B, 0x54F76BDB */
+    7.322_342_659_630_793e-2,   /* 0x3FB2BEC5, 0x3E883E34 */
+    1.998_191_740_938_16,       /* 0x3FFFF897, 0xE727779C */
+    1.449_560_293_478_857_4e1,  /* 0x402CFDBF, 0xAAF96FE5 */
+    3.166_623_175_047_815_4e1,  /* 0x403FAA8E, 0x29FBDC4A */
+    1.625_270_757_109_292_7e1,  /* 0x403040B1, 0x71814BB4 */
+];
+const Q_S2: [f64; 6] = [
+    3.036_558_483_552_192e1,   /* 0x403E5D96, 0xF7C07AED */
+    2.693_481_186_080_498_4e2, /* 0x4070D591, 0xE4D14B40 */
+    8.447_837_575_953_201e2,   /* 0x408A6645, 0x22B3BF22 */
+    8.829_358_451_124_886e2,   /* 0x408B977C, 0x9C5CC214 */
+    2.126_663_885_117_988_3e2, /* 0x406A9553, 0x0E001365 */
+    -5.310_954_938_826_669_5,  /* 0xC0153E6A, 0xF8B32931 */
+];
+
+fn qzero(x: f64) -> f64 {
+    let ix = high_word(x) & 0x7fffffff;
+    let (p, q) = if ix >= 0x40200000 {
+        (Q_R8, Q_S8)
+    } else if ix >= 0x40122E8B {
+        (Q_R5, Q_S5)
+    } else if ix >= 0x4006DB6D {
+        (Q_R3, Q_S3)
+    } else {
+        (Q_R2, Q_S2)
+    };
+    let z = 1.0 / (x * x);
+    let r = p[0] + z * (p[1] + z * (p[2] + z * (p[3] + z * (p[4] + z * p[5]))));
+    let s = 1.0 + z * (q[0] + z * (q[1] + z * (q[2] + z * (q[3] + z * (q[4] + z * q[5])))));
+    (-0.125 + r / s) / x
+}
+
+const U00: f64 = -7.380_429_510_868_723e-2; /* 0xBFB2E4D6, 0x99CBD01F */
+const U01: f64 = 1.766_664_525_091_811_2e-1; /* 0x3FC69D01, 0x9DE9E3FC */
+const U02: f64 = -1.381_856_719_455_969e-2; /* 0xBF8C4CE8, 0xB16CFA97 */
+const U03: f64 = 3.474_534_320_936_836_5e-4; /* 0x3F36C54D, 0x20B29B6B */
+const U04: f64 = -3.814_070_537_243_641_6e-6; /* 0xBECFFEA7, 0x73D25CAD */
+const U05: f64 = 1.955_901_370_350_229_2e-8; /* 0x3E550057, 0x3B4EABD4 */
+const U06: f64 = -3.982_051_941_321_034e-11; /* 0xBDC5E43D, 0x693FB3C8 */
+const V01: f64 = 1.273_048_348_341_237e-2; /* 0x3F8A1270, 0x91C9C71A */
+const V02: f64 = 7.600_686_273_503_533e-5; /* 0x3F13ECBB, 0xF578C6C1 */
+const V03: f64 = 2.591_508_518_404_578e-7; /* 0x3E91642D, 0x7FF202FD */
+const V04: f64 = 4.411_103_113_326_754_7e-10; /* 0x3DFE5018, 0x3BD6D9EF */
+
+pub(crate) fn y0(x: f64) -> f64 {
+    let (lx, hx) = split_words(x);
+    let ix = 0x7fffffff & hx;
+
+    // Y0(NaN) is NaN, y0(-inf) is Nan, y0(inf) is 0
+    if ix >= 0x7ff00000 {
+        return 1.0 / (x + x * x);
+    }
+    if (ix | lx) == 0 {
+        return f64::NEG_INFINITY;
+    }
+    if hx < 0 {
+        return f64::NAN;
+    }
+
+    if ix >= 0x40000000 {
+        // |x| >= 2.0
+        // y0(x) = sqrt(2/(pi*x))*(p0(x)*sin(x0)+q0(x)*cos(x0))
+        // where x0 = x-pi/4
+        //      Better formula:
+        //              cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4)
+        //                      =  1/sqrt(2) * (sin(x) + cos(x))
+        //              sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
+        //                      =  1/sqrt(2) * (sin(x) - cos(x))
+        // To avoid cancellation, use
+        //              sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x))
+        // to compute the worse 1.
+
+        let s = x.sin();
+        let c = x.cos();
+        let mut ss = s - c;
+        let mut cc = s + c;
+
+        // j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x)
+        // y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x)
+
+        if ix < 0x7fe00000 {
+            // make sure x+x not overflow
+            let z = -(x + x).cos();
+            if (s * c) < 0.0 {
+                cc = z / ss;
+            } else {
+                ss = z / cc;
+            }
+        }
+        return if ix > 0x48000000 {
+            FRAC_2_SQRT_PI * ss / x.sqrt()
+        } else {
+            let u = pzero(x);
+            let v = qzero(x);
+            FRAC_2_SQRT_PI * (u * ss + v * cc) / x.sqrt()
+        };
+    }
+
+    if ix <= 0x3e400000 {
+        // x < 2^(-27)
+        return U00 + FRAC_2_PI * x.ln();
+    }
+    let z = x * x;
+    let u = U00 + z * (U01 + z * (U02 + z * (U03 + z * (U04 + z * (U05 + z * U06)))));
+    let v = 1.0 + z * (V01 + z * (V02 + z * (V03 + z * V04)));
+    u / v + FRAC_2_PI * (j0(x) * x.ln())
+}
+
+pub(crate) fn j0(x: f64) -> f64 {
+    let hx = high_word(x);
+    let ix = hx & 0x7fffffff;
+    if x.is_nan() {
+        return x;
+    } else if x.is_infinite() {
+        return 0.0;
+    }
+    // the function is even
+    let x = x.abs();
+    if ix >= 0x40000000 {
+        // |x| >= 2.0
+        // let (s, c) = x.sin_cos()
+        let s = x.sin();
+        let c = x.cos();
+        let mut ss = s - c;
+        let mut cc = s + c;
+        // makes sure that x+x does not overflow
+        if ix < 0x7fe00000 {
+            // |x| < f64::MAX / 2.0
+            let z = -(x + x).cos();
+            if s * c < 0.0 {
+                cc = z / ss;
+            } else {
+                ss = z / cc;
+            }
+        }
+
+        //   j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x)
+        //   y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x)
+        return if ix > 0x48000000 {
+            // x < 5.253807105661922e-287 (2^(-951))
+            FRAC_2_SQRT_PI * cc / (x.sqrt())
+        } else {
+            let u = pzero(x);
+            let v = qzero(x);
+            FRAC_2_SQRT_PI * (u * cc - v * ss) / x.sqrt()
+        };
+    };
+    if ix < 0x3f200000 {
+        // |x| < 2^(-13)
+        if HUGE + x > 1.0 {
+            // raise inexact if x != 0
+            if ix < 0x3e400000 {
+                return 1.0; // |x|<2^(-27)
+            } else {
+                return 1.0 - 0.25 * x * x;
+            }
+        }
+    }
+    let z = x * x;
+    let r = z * (R02 + z * (R03 + z * (R04 + z * R05)));
+    let s = 1.0 + z * (S01 + z * (S02 + z * (S03 + z * S04)));
+    if ix < 0x3FF00000 {
+        // |x| < 1.00
+        1.0 + z * (-0.25 + (r / s))
+    } else {
+        let u = 0.5 * x;
+        (1.0 + u) * (1.0 - u) + z * (r / s)
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/transcendental/bessel_j1_y1.rs.html b/src/ironcalc_base/functions/engineering/transcendental/bessel_j1_y1.rs.html new file mode 100644 index 0000000..12513e2 --- /dev/null +++ b/src/ironcalc_base/functions/engineering/transcendental/bessel_j1_y1.rs.html @@ -0,0 +1,783 @@ +bessel_j1_y1.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+
/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+/* __ieee754_j1(x), __ieee754_y1(x)
+ * Bessel function of the first and second kinds of order zero.
+ * Method -- j1(x):
+ *	1. For tiny x, we use j1(x) = x/2 - x^3/16 + x^5/384 - ...
+ *	2. Reduce x to |x| since j1(x)=-j1(-x),  and
+ *	   for x in (0,2)
+ *		j1(x) = x/2 + x*z*R0/S0,  where z = x*x;
+ *	   (precision:  |j1/x - 1/2 - R0/S0 |<2**-61.51 )
+ *	   for x in (2,inf)
+ * 		j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
+ * 		y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1))
+ * 	   where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1)
+ *	   as follow:
+ *		cos(x1) =  cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
+ *			=  1/sqrt(2) * (sin(x) - cos(x))
+ *		sin(x1) =  sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
+ *			= -1/sqrt(2) * (sin(x) + cos(x))
+ * 	   (To avoid cancellation, use
+ *		sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x))
+ * 	    to compute the worse one.)
+ *
+ *	3 Special cases
+ *		j1(nan)= nan
+ *		j1(0) = 0
+ *		j1(inf) = 0
+ *
+ * Method -- y1(x):
+ *	1. screen out x<=0 cases: y1(0)=-inf, y1(x<0)=NaN
+ *	2. For x<2.
+ *	   Since
+ *		y1(x) = 2/pi*(j1(x)*(ln(x/2)+Euler)-1/x-x/2+5/64*x^3-...)
+ *	   therefore y1(x)-2/pi*j1(x)*ln(x)-1/x is an odd function.
+ *	   We use the following function to approximate y1,
+ *		y1(x) = x*U(z)/V(z) + (2/pi)*(j1(x)*ln(x)-1/x), z= x^2
+ *	   where for x in [0,2] (abs err less than 2**-65.89)
+ *		U(z) = U0[0] + U0[1]*z + ... + U0[4]*z^4
+ *		V(z) = 1  + v0[0]*z + ... + v0[4]*z^5
+ *	   Note: For tiny x, 1/x dominate y1 and hence
+ *		y1(tiny) = -2/pi/tiny, (choose tiny<2**-54)
+ *	3. For x>=2.
+ * 		y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1))
+ * 	   where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1)
+ *	   by method mentioned above.
+ */
+
+use std::f64::consts::FRAC_2_PI;
+
+use super::bessel_util::{high_word, split_words, FRAC_2_SQRT_PI, HUGE};
+
+// R0/S0 on [0,2]
+const R00: f64 = -6.25e-2; // 0xBFB00000, 0x00000000
+const R01: f64 = 1.407_056_669_551_897e-3; // 0x3F570D9F, 0x98472C61
+const R02: f64 = -1.599_556_310_840_356e-5; // 0xBEF0C5C6, 0xBA169668
+const R03: f64 = 4.967_279_996_095_844_5e-8; // 0x3E6AAAFA, 0x46CA0BD9
+const S01: f64 = 1.915_375_995_383_634_6e-2; // 0x3F939D0B, 0x12637E53
+const S02: f64 = 1.859_467_855_886_309_2e-4; // 0x3F285F56, 0xB9CDF664
+const S03: f64 = 1.177_184_640_426_236_8e-6; // 0x3EB3BFF8, 0x333F8498
+const S04: f64 = 5.046_362_570_762_170_4e-9; // 0x3E35AC88, 0xC97DFF2C
+const S05: f64 = 1.235_422_744_261_379_1e-11; // 0x3DAB2ACF, 0xCFB97ED8
+
+pub(crate) fn j1(x: f64) -> f64 {
+    let hx = high_word(x);
+    let ix = hx & 0x7fffffff;
+    if ix >= 0x7ff00000 {
+        return 1.0 / x;
+    }
+    let y = x.abs();
+    if ix >= 0x40000000 {
+        /* |x| >= 2.0 */
+        let s = y.sin();
+        let c = y.cos();
+        let mut ss = -s - c;
+        let mut cc = s - c;
+        if ix < 0x7fe00000 {
+            /* make sure y+y not overflow */
+            let z = (y + y).cos();
+            if s * c > 0.0 {
+                cc = z / ss;
+            } else {
+                ss = z / cc;
+            }
+        }
+
+        // j1(x) = 1/sqrt(pi) * (P(1,x)*cc - Q(1,x)*ss) / sqrt(x)
+        // y1(x) = 1/sqrt(pi) * (P(1,x)*ss + Q(1,x)*cc) / sqrt(x)
+
+        let z = if ix > 0x48000000 {
+            FRAC_2_SQRT_PI * cc / y.sqrt()
+        } else {
+            let u = pone(y);
+            let v = qone(y);
+            FRAC_2_SQRT_PI * (u * cc - v * ss) / y.sqrt()
+        };
+        if hx < 0 {
+            return -z;
+        } else {
+            return z;
+        }
+    }
+    if ix < 0x3e400000 {
+        /* |x|<2**-27 */
+        if HUGE + x > 1.0 {
+            return 0.5 * x; /* inexact if x!=0 necessary */
+        }
+    }
+    let z = x * x;
+    let mut r = z * (R00 + z * (R01 + z * (R02 + z * R03)));
+    let s = 1.0 + z * (S01 + z * (S02 + z * (S03 + z * (S04 + z * S05))));
+    r *= x;
+    x * 0.5 + r / s
+}
+
+const U0: [f64; 5] = [
+    -1.960_570_906_462_389_4e-1, /* 0xBFC91866, 0x143CBC8A */
+    5.044_387_166_398_113e-2,    /* 0x3FA9D3C7, 0x76292CD1 */
+    -1.912_568_958_757_635_5e-3, /* 0xBF5F55E5, 0x4844F50F */
+    2.352_526_005_616_105e-5,    /* 0x3EF8AB03, 0x8FA6B88E */
+    -9.190_991_580_398_789e-8,   /* 0xBE78AC00, 0x569105B8 */
+];
+const V0: [f64; 5] = [
+    1.991_673_182_366_499e-2,    /* 0x3F94650D, 0x3F4DA9F0 */
+    2.025_525_810_251_351_7e-4,  /* 0x3F2A8C89, 0x6C257764 */
+    1.356_088_010_975_162_3e-6,  /* 0x3EB6C05A, 0x894E8CA6 */
+    6.227_414_523_646_215e-9,    /* 0x3E3ABF1D, 0x5BA69A86 */
+    1.665_592_462_079_920_8e-11, /* 0x3DB25039, 0xDACA772A */
+];
+
+pub(crate) fn y1(x: f64) -> f64 {
+    let (lx, hx) = split_words(x);
+    let ix = 0x7fffffff & hx;
+    // if Y1(NaN) is NaN, Y1(-inf) is NaN, Y1(inf) is 0
+    if ix >= 0x7ff00000 {
+        return 1.0 / (x + x * x);
+    }
+    if (ix | lx) == 0 {
+        return f64::NEG_INFINITY;
+    }
+    if hx < 0 {
+        return f64::NAN;
+    }
+    if ix >= 0x40000000 {
+        // |x| >= 2.0
+        let s = x.sin();
+        let c = x.cos();
+        let mut ss = -s - c;
+        let mut cc = s - c;
+        if ix < 0x7fe00000 {
+            // make sure x+x not overflow
+            let z = (x + x).cos();
+            if s * c > 0.0 {
+                cc = z / ss;
+            } else {
+                ss = z / cc;
+            }
+        }
+        /* y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x0)+q1(x)*cos(x0))
+         * where x0 = x-3pi/4
+         *      Better formula:
+         *              cos(x0) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
+         *                      =  1/sqrt(2) * (sin(x) - cos(x))
+         *              sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
+         *                      = -1/sqrt(2) * (cos(x) + sin(x))
+         * To avoid cancellation, use
+         *              sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x))
+         * to compute the worse one.
+         */
+        return if ix > 0x48000000 {
+            FRAC_2_SQRT_PI * ss / x.sqrt()
+        } else {
+            let u = pone(x);
+            let v = qone(x);
+            FRAC_2_SQRT_PI * (u * ss + v * cc) / x.sqrt()
+        };
+    }
+    if ix <= 0x3c900000 {
+        // x < 2^(-54)
+        return -FRAC_2_PI / x;
+    }
+    let z = x * x;
+    let u = U0[0] + z * (U0[1] + z * (U0[2] + z * (U0[3] + z * U0[4])));
+    let v = 1.0 + z * (V0[0] + z * (V0[1] + z * (V0[2] + z * (V0[3] + z * V0[4]))));
+    x * (u / v) + FRAC_2_PI * (j1(x) * x.ln() - 1.0 / x)
+}
+
+/* For x >= 8, the asymptotic expansions of pone is
+ *	1 + 15/128 s^2 - 4725/2^15 s^4 - ...,	where s = 1/x.
+ * We approximate pone by
+ * 	pone(x) = 1 + (R/S)
+ * where  R = pr0 + pr1*s^2 + pr2*s^4 + ... + pr5*s^10
+ * 	  S = 1 + ps0*s^2 + ... + ps4*s^10
+ * and
+ *	| pone(x)-1-R/S | <= 2  ** ( -60.06)
+ */
+
+const PR8: [f64; 6] = [
+    /* for x in [inf, 8]=1/[0,0.125] */
+    0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */
+    1.171_874_999_999_886_5e-1, /* 0x3FBDFFFF, 0xFFFFFCCE */
+    1.323_948_065_930_735_8e1,  /* 0x402A7A9D, 0x357F7FCE */
+    4.120_518_543_073_785_6e2,  /* 0x4079C0D4, 0x652EA590 */
+    3.874_745_389_139_605_3e3,  /* 0x40AE457D, 0xA3A532CC */
+    7.914_479_540_318_917e3,    /* 0x40BEEA7A, 0xC32782DD */
+];
+
+const PS8: [f64; 5] = [
+    1.142_073_703_756_784_1e2, /* 0x405C8D45, 0x8E656CAC */
+    3.650_930_834_208_534_6e3, /* 0x40AC85DC, 0x964D274F */
+    3.695_620_602_690_334_6e4, /* 0x40E20B86, 0x97C5BB7F */
+    9.760_279_359_349_508e4,   /* 0x40F7D42C, 0xB28F17BB */
+    3.080_427_206_278_888e4,   /* 0x40DE1511, 0x697A0B2D */
+];
+
+const PR5: [f64; 6] = [
+    /* for x in [8,4.5454]=1/[0.125,0.22001] */
+    1.319_905_195_562_435_2e-11, /* 0x3DAD0667, 0xDAE1CA7D */
+    1.171_874_931_906_141e-1,    /* 0x3FBDFFFF, 0xE2C10043 */
+    6.802_751_278_684_329,       /* 0x401B3604, 0x6E6315E3 */
+    1.083_081_829_901_891_1e2,   /* 0x405B13B9, 0x452602ED */
+    5.176_361_395_331_998e2,     /* 0x40802D16, 0xD052D649 */
+    5.287_152_013_633_375e2,     /* 0x408085B8, 0xBB7E0CB7 */
+];
+const PS5: [f64; 5] = [
+    5.928_059_872_211_313e1,   /* 0x404DA3EA, 0xA8AF633D */
+    9.914_014_187_336_144e2,   /* 0x408EFB36, 0x1B066701 */
+    5.353_266_952_914_88e3,    /* 0x40B4E944, 0x5706B6FB */
+    7.844_690_317_495_512e3,   /* 0x40BEA4B0, 0xB8A5BB15 */
+    1.504_046_888_103_610_6e3, /* 0x40978030, 0x036F5E51 */
+];
+
+const PR3: [f64; 6] = [
+    3.025_039_161_373_736e-9,   /* 0x3E29FC21, 0xA7AD9EDD */
+    1.171_868_655_672_535_9e-1, /* 0x3FBDFFF5, 0x5B21D17B */
+    3.932_977_500_333_156_4,    /* 0x400F76BC, 0xE85EAD8A */
+    3.511_940_355_916_369e1,    /* 0x40418F48, 0x9DA6D129 */
+    9.105_501_107_507_813e1,    /* 0x4056C385, 0x4D2C1837 */
+    4.855_906_851_973_649e1,    /* 0x4048478F, 0x8EA83EE5 */
+];
+const PS3: [f64; 5] = [
+    3.479_130_950_012_515e1,   /* 0x40416549, 0xA134069C */
+    3.367_624_587_478_257_5e2, /* 0x40750C33, 0x07F1A75F */
+    1.046_871_399_757_751_3e3, /* 0x40905B7C, 0x5037D523 */
+    8.908_113_463_982_564e2,   /* 0x408BD67D, 0xA32E31E9 */
+    1.037_879_324_396_392_8e2, /* 0x4059F26D, 0x7C2EED53 */
+];
+
+const PR2: [f64; 6] = [
+    /* for x in [2.8570,2]=1/[0.3499,0.5] */
+    1.077_108_301_068_737_4e-7, /* 0x3E7CE9D4, 0xF65544F4 */
+    1.171_762_194_626_833_5e-1, /* 0x3FBDFF42, 0xBE760D83 */
+    2.368_514_966_676_088,      /* 0x4002F2B7, 0xF98FAEC0 */
+    1.224_261_091_482_612_3e1,  /* 0x40287C37, 0x7F71A964 */
+    1.769_397_112_716_877_3e1,  /* 0x4031B1A8, 0x177F8EE2 */
+    5.073_523_125_888_185,      /* 0x40144B49, 0xA574C1FE */
+];
+const PS2: [f64; 5] = [
+    2.143_648_593_638_214e1,   /* 0x40356FBD, 0x8AD5ECDC */
+    1.252_902_271_684_027_5e2, /* 0x405F5293, 0x14F92CD5 */
+    2.322_764_690_571_628e2,   /* 0x406D08D8, 0xD5A2DBD9 */
+    1.176_793_732_871_471e2,   /* 0x405D6B7A, 0xDA1884A9 */
+    8.364_638_933_716_183,     /* 0x4020BAB1, 0xF44E5192 */
+];
+
+/* Note: This function is only called for ix>=0x40000000 (see above) */
+fn pone(x: f64) -> f64 {
+    let ix = high_word(x) & 0x7fffffff;
+    // ix>=0x40000000 && ix<=0x48000000)
+    let (p, q) = if ix >= 0x40200000 {
+        (PR8, PS8)
+    } else if ix >= 0x40122E8B {
+        (PR5, PS5)
+    } else if ix >= 0x4006DB6D {
+        (PR3, PS3)
+    } else {
+        (PR2, PS2)
+    };
+    let z = 1.0 / (x * x);
+    let r = p[0] + z * (p[1] + z * (p[2] + z * (p[3] + z * (p[4] + z * p[5]))));
+    let s = 1.0 + z * (q[0] + z * (q[1] + z * (q[2] + z * (q[3] + z * q[4]))));
+    1.0 + r / s
+}
+
+/* For x >= 8, the asymptotic expansions of qone is
+ *	3/8 s - 105/1024 s^3 - ..., where s = 1/x.
+ * We approximate pone by
+ * 	qone(x) = s*(0.375 + (R/S))
+ * where  R = qr1*s^2 + qr2*s^4 + ... + qr5*s^10
+ * 	  S = 1 + qs1*s^2 + ... + qs6*s^12
+ * and
+ *	| qone(x)/s -0.375-R/S | <= 2  ** ( -61.13)
+ */
+
+const QR8: [f64; 6] = [
+    /* for x in [inf, 8]=1/[0,0.125] */
+    0.00000000000000000000e+00,  /* 0x00000000, 0x00000000 */
+    -1.025_390_624_999_927_1e-1, /* 0xBFBA3FFF, 0xFFFFFDF3 */
+    -1.627_175_345_445_9e1,      /* 0xC0304591, 0xA26779F7 */
+    -7.596_017_225_139_501e2,    /* 0xC087BCD0, 0x53E4B576 */
+    -1.184_980_667_024_295_9e4,  /* 0xC0C724E7, 0x40F87415 */
+    -4.843_851_242_857_503_5e4,  /* 0xC0E7A6D0, 0x65D09C6A */
+];
+const QS8: [f64; 6] = [
+    1.613_953_697_007_229e2,    /* 0x40642CA6, 0xDE5BCDE5 */
+    7.825_385_999_233_485e3,    /* 0x40BE9162, 0xD0D88419 */
+    1.338_753_362_872_495_8e5,  /* 0x4100579A, 0xB0B75E98 */
+    7.196_577_236_832_409e5,    /* 0x4125F653, 0x72869C19 */
+    6.666_012_326_177_764e5,    /* 0x412457D2, 0x7719AD5C */
+    -2.944_902_643_038_346_4e5, /* 0xC111F969, 0x0EA5AA18 */
+];
+
+const QR5: [f64; 6] = [
+    /* for x in [8,4.5454]=1/[0.125,0.22001] */
+    -2.089_799_311_417_641e-11,  /* 0xBDB6FA43, 0x1AA1A098 */
+    -1.025_390_502_413_754_3e-1, /* 0xBFBA3FFF, 0xCB597FEF */
+    -8.056_448_281_239_36,       /* 0xC0201CE6, 0xCA03AD4B */
+    -1.836_696_074_748_883_8e2,  /* 0xC066F56D, 0x6CA7B9B0 */
+    -1.373_193_760_655_081_6e3,  /* 0xC09574C6, 0x6931734F */
+    -2.612_444_404_532_156_6e3,  /* 0xC0A468E3, 0x88FDA79D */
+];
+const QS5: [f64; 6] = [
+    8.127_655_013_843_358e1,   /* 0x405451B2, 0xFF5A11B2 */
+    1.991_798_734_604_859_6e3, /* 0x409F1F31, 0xE77BF839 */
+    1.746_848_519_249_089e4,   /* 0x40D10F1F, 0x0D64CE29 */
+    4.985_142_709_103_523e4,   /* 0x40E8576D, 0xAABAD197 */
+    2.794_807_516_389_181_2e4, /* 0x40DB4B04, 0xCF7C364B */
+    -4.719_183_547_951_285e3,  /* 0xC0B26F2E, 0xFCFFA004 */
+];
+
+const QR3: [f64; 6] = [
+    -5.078_312_264_617_666e-9,   /* 0xBE35CFA9, 0xD38FC84F */
+    -1.025_378_298_208_370_9e-1, /* 0xBFBA3FEB, 0x51AEED54 */
+    -4.610_115_811_394_734,      /* 0xC01270C2, 0x3302D9FF */
+    -5.784_722_165_627_836_4e1,  /* 0xC04CEC71, 0xC25D16DA */
+    -2.282_445_407_376_317e2,    /* 0xC06C87D3, 0x4718D55F */
+    -2.192_101_284_789_093_3e2,  /* 0xC06B66B9, 0x5F5C1BF6 */
+];
+const QS3: [f64; 6] = [
+    4.766_515_503_237_295e1,    /* 0x4047D523, 0xCCD367E4 */
+    6.738_651_126_766_997e2,    /* 0x40850EEB, 0xC031EE3E */
+    3.380_152_866_795_263_4e3,  /* 0x40AA684E, 0x448E7C9A */
+    5.547_729_097_207_228e3,    /* 0x40B5ABBA, 0xA61D54A6 */
+    1.903_119_193_388_108e3,    /* 0x409DBC7A, 0x0DD4DF4B */
+    -1.352_011_914_443_073_4e2, /* 0xC060E670, 0x290A311F */
+];
+
+const QR2: [f64; 6] = [
+    /* for x in [2.8570,2]=1/[0.3499,0.5] */
+    -1.783_817_275_109_588_7e-7, /* 0xBE87F126, 0x44C626D2 */
+    -1.025_170_426_079_855_5e-1, /* 0xBFBA3E8E, 0x9148B010 */
+    -2.752_205_682_781_874_6,    /* 0xC0060484, 0x69BB4EDA */
+    -1.966_361_626_437_037_2e1,  /* 0xC033A9E2, 0xC168907F */
+    -4.232_531_333_728_305e1,    /* 0xC04529A3, 0xDE104AAA */
+    -2.137_192_117_037_040_6e1,  /* 0xC0355F36, 0x39CF6E52 */
+];
+const QS2: [f64; 6] = [
+    2.953_336_290_605_238_5e1, /* 0x403D888A, 0x78AE64FF */
+    2.529_815_499_821_905_3e2, /* 0x406F9F68, 0xDB821CBA */
+    7.575_028_348_686_454e2,   /* 0x4087AC05, 0xCE49A0F7 */
+    7.393_932_053_204_672e2,   /* 0x40871B25, 0x48D4C029 */
+    1.559_490_033_366_661_2e2, /* 0x40637E5E, 0x3C3ED8D4 */
+    -4.959_498_988_226_282,    /* 0xC013D686, 0xE71BE86B */
+];
+
+// Note: This function is only called for ix>=0x40000000 (see above)
+fn qone(x: f64) -> f64 {
+    let ix = high_word(x) & 0x7fffffff;
+    // ix>=0x40000000 && ix<=0x48000000
+    let (p, q) = if ix >= 0x40200000 {
+        (QR8, QS8)
+    } else if ix >= 0x40122E8B {
+        (QR5, QS5)
+    } else if ix >= 0x4006DB6D {
+        (QR3, QS3)
+    } else {
+        (QR2, QS2)
+    };
+    let z = 1.0 / (x * x);
+    let r = p[0] + z * (p[1] + z * (p[2] + z * (p[3] + z * (p[4] + z * p[5]))));
+    let s = 1.0 + z * (q[0] + z * (q[1] + z * (q[2] + z * (q[3] + z * (q[4] + z * q[5])))));
+    (0.375 + r / s) / x
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/transcendental/bessel_jn_yn.rs.html b/src/ironcalc_base/functions/engineering/transcendental/bessel_jn_yn.rs.html new file mode 100644 index 0000000..0743d52 --- /dev/null +++ b/src/ironcalc_base/functions/engineering/transcendental/bessel_jn_yn.rs.html @@ -0,0 +1,659 @@ +bessel_jn_yn.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+
// https://github.com/JuliaLang/openlibm/blob/master/src/e_jn.c
+
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+/*
+ * __ieee754_jn(n, x), __ieee754_yn(n, x)
+ * floating point Bessel's function of the 1st and 2nd kind
+ * of order n
+ *
+ * Special cases:
+ *	y0(0)=y1(0)=yn(n,0) = -inf with division by 0 signal;
+ *	y0(-ve)=y1(-ve)=yn(n,-ve) are NaN with invalid signal.
+ * Note 2. About jn(n,x), yn(n,x)
+ *	For n=0, j0(x) is called,
+ *	for n=1, j1(x) is called,
+ *	for n<x, forward recursion us used starting
+ *	from values of j0(x) and j1(x).
+ *	for n>x, a continued fraction approximation to
+ *	j(n,x)/j(n-1,x) is evaluated and then backward
+ *	recursion is used starting from a supposed value
+ *	for j(n,x). The resulting value of j(0,x) is
+ *	compared with the actual value to correct the
+ *	supposed value of j(n,x).
+ *
+ *	yn(n,x) is similar in all respects, except
+ *	that forward recursion is used for all
+ *	values of n>1.
+ *
+ */
+
+use super::{
+    bessel_j0_y0::{j0, y0},
+    bessel_j1_y1::{j1, y1},
+    bessel_util::{split_words, FRAC_2_SQRT_PI},
+};
+
+// Special cases are:
+//
+//	$ J_n(n, ±\Infinity) = 0$
+//	$ J_n(n, NaN} = NaN $
+//  $ J_n(n, 0) = 0 $
+pub(crate) fn jn(n: i32, x: f64) -> f64 {
+    let (lx, mut hx) = split_words(x);
+    let ix = 0x7fffffff & hx;
+    // if J(n,NaN) is NaN
+    if x.is_nan() {
+        return x;
+    }
+    // if (ix | (/*(u_int32_t)*/(lx | -lx)) >> 31) > 0x7ff00000 {
+    //     return x + x;
+    // }
+    let (n, x) = if n < 0 {
+        // hx ^= 0x80000000;
+        hx = -hx;
+        (-n, -x)
+    } else {
+        (n, x)
+    };
+    if n == 0 {
+        return j0(x);
+    }
+    if n == 1 {
+        return j1(x);
+    }
+    let sign = (n & 1) & (hx >> 31); /* even n -- 0, odd n -- sign(x) */
+    // let sign = if x < 0.0 { -1 } else { 1 };
+    let x = x.abs();
+    let b = if (ix | lx) == 0 || ix >= 0x7ff00000 {
+        // if x is 0 or inf
+        0.0
+    } else if n as f64 <= x {
+        /* Safe to use J(n+1,x)=2n/x *J(n,x)-J(n-1,x) */
+        if ix >= 0x52D00000 {
+            /* x > 2**302 */
+            /* (x >> n**2)
+             *	    Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi)
+             *	    Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi)
+             *	    Let s=x.sin(), c=x.cos(),
+             *		xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then
+             *
+             *		   n	sin(xn)*sqt2	cos(xn)*sqt2
+             *		----------------------------------
+             *		   0	 s-c		 c+s
+             *		   1	-s-c 		-c+s
+             *		   2	-s+c		-c-s
+             *		   3	 s+c		 c-s
+             */
+            let temp = match n & 3 {
+                0 => x.cos() + x.sin(),
+                1 => -x.cos() + x.sin(),
+                2 => -x.cos() - x.sin(),
+                3 => x.cos() - x.sin(),
+                _ => {
+                    // Impossible: FIXME!
+                    // panic!("")
+                    0.0
+                }
+            };
+            FRAC_2_SQRT_PI * temp / x.sqrt()
+        } else {
+            let mut a = j0(x);
+            let mut b = j1(x);
+            for i in 1..n {
+                let temp = b;
+                b = b * (((i + i) as f64) / x) - a; /* avoid underflow */
+                a = temp;
+            }
+            b
+        }
+    } else {
+        // x < 2^(-29)
+        if ix < 0x3e100000 {
+            // x is tiny, return the first Taylor expansion of J(n,x)
+            // J(n,x) = 1/n!*(x/2)^n  - ...
+            if n > 33 {
+                // underflow
+                0.0
+            } else {
+                let temp = x * 0.5;
+                let mut b = temp;
+                let mut a = 1;
+                for i in 2..=n {
+                    a *= i; /* a = n! */
+                    b *= temp; /* b = (x/2)^n */
+                }
+                b / (a as f64)
+            }
+        } else {
+            /* use backward recurrence */
+            /* 			x      x^2      x^2
+             *  J(n,x)/J(n-1,x) =  ----   ------   ------   .....
+             *			2n  - 2(n+1) - 2(n+2)
+             *
+             * 			1      1        1
+             *  (for large x)   =  ----  ------   ------   .....
+             *			2n   2(n+1)   2(n+2)
+             *			-- - ------ - ------ -
+             *			 x     x         x
+             *
+             * Let w = 2n/x and h=2/x, then the above quotient
+             * is equal to the continued fraction:
+             *		    1
+             *	= -----------------------
+             *		       1
+             *	   w - -----------------
+             *			  1
+             * 	        w+h - ---------
+             *		       w+2h - ...
+             *
+             * To determine how many terms needed, let
+             * Q(0) = w, Q(1) = w(w+h) - 1,
+             * Q(k) = (w+k*h)*Q(k-1) - Q(k-2),
+             * When Q(k) > 1e4	good for single
+             * When Q(k) > 1e9	good for double
+             * When Q(k) > 1e17	good for quadruple
+             */
+
+            let w = ((n + n) as f64) / x;
+            let h = 2.0 / x;
+            let mut q0 = w;
+            let mut z = w + h;
+            let mut q1 = w * z - 1.0;
+            let mut k = 1;
+            while q1 < 1.0e9 {
+                k += 1;
+                z += h;
+                let tmp = z * q1 - q0;
+                q0 = q1;
+                q1 = tmp;
+            }
+            let m = n + n;
+            let mut t = 0.0;
+            for i in (m..2 * (n + k)).step_by(2).rev() {
+                t = 1.0 / ((i as f64) / x - t);
+            }
+            // for (t=0, i = 2*(n+k); i>=m; i -= 2) t = 1/(i/x-t);
+            let mut a = t;
+            let mut b = 1.0;
+            /*  estimate log((2/x)^n*n!) = n*log(2/x)+n*ln(n)
+             *  Hence, if n*(log(2n/x)) > ...
+             *  single 8.8722839355e+01
+             *  double 7.09782712893383973096e+02
+             *  long double 1.1356523406294143949491931077970765006170e+04
+             *  then recurrent value may overflow and the result is
+             *  likely underflow to 0
+             */
+            let mut tmp = n as f64;
+            let v = 2.0 / x;
+            tmp = tmp * f64::ln(f64::abs(v * tmp));
+            if tmp < 7.097_827_128_933_84e2 {
+                // for(i=n-1, di=(i+i); i>0; i--){
+                let mut di = 2.0 * ((n - 1) as f64);
+                for _ in (1..=n - 1).rev() {
+                    let temp = b;
+                    b *= di;
+                    b = b / x - a;
+                    a = temp;
+                    di -= 2.0;
+                }
+            } else {
+                // for(i=n-1, di=(i+i); i>0; i--) {
+                let mut di = 2.0 * ((n - 1) as f64);
+                for _ in (1..=n - 1).rev() {
+                    let temp = b;
+                    b *= di;
+                    b = b / x - a;
+                    a = temp;
+                    di -= 2.0;
+                    /* scale b to avoid spurious overflow */
+                    if b > 1e100 {
+                        a /= b;
+                        t /= b;
+                        b = 1.0;
+                    }
+                }
+            }
+            let z = j0(x);
+            let w = j1(x);
+            if z.abs() >= w.abs() {
+                t * z / b
+            } else {
+                t * w / a
+            }
+        }
+    };
+    if sign == 1 {
+        -b
+    } else {
+        b
+    }
+}
+
+// Yn returns the order-n Bessel function of the second kind.
+//
+// Special cases are:
+//
+//	Y(n, +Inf) = 0
+//	Y(n ≥ 0, 0) = -Inf
+//	Y(n < 0, 0) = +Inf if n is odd, -Inf if n is even
+//	Y(n, x < 0) = NaN
+//	Y(n, NaN) = NaN
+pub(crate) fn yn(n: i32, x: f64) -> f64 {
+    let (lx, hx) = split_words(x);
+    let ix = 0x7fffffff & hx;
+
+    // if Y(n, NaN) is NaN
+    if x.is_nan() {
+        return x;
+    }
+    // if (ix | (/*(u_int32_t)*/(lx | -lx)) >> 31) > 0x7ff00000 {
+    //     return x + x;
+    // }
+
+    if (ix | lx) == 0 {
+        return f64::NEG_INFINITY;
+    }
+    if hx < 0 {
+        return f64::NAN;
+    }
+
+    let (n, sign) = if n < 0 {
+        (-n, 1 - ((n & 1) << 1))
+    } else {
+        (n, 1)
+    };
+    if n == 0 {
+        return y0(x);
+    }
+    if n == 1 {
+        return (sign as f64) * y1(x);
+    }
+    if ix == 0x7ff00000 {
+        return 0.0;
+    }
+    let b = if ix >= 0x52D00000 {
+        // x > 2^302
+        /* (x >> n**2)
+         *	    Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi)
+         *	    Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi)
+         *	    Let s=x.sin(), c=x.cos(),
+         *		xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then
+         *
+         *		   n	sin(xn)*sqt2	cos(xn)*sqt2
+         *		----------------------------------
+         *		   0	 s-c		 c+s
+         *		   1	-s-c 		-c+s
+         *		   2	-s+c		-c-s
+         *		   3	 s+c		 c-s
+         */
+        let temp = match n & 3 {
+            0 => x.sin() - x.cos(),
+            1 => -x.sin() - x.cos(),
+            2 => -x.sin() + x.cos(),
+            3 => x.sin() + x.cos(),
+            _ => {
+                // unreachable
+                0.0
+            }
+        };
+        FRAC_2_SQRT_PI * temp / x.sqrt()
+    } else {
+        let mut a = y0(x);
+        let mut b = y1(x);
+        for i in 1..n {
+            if b.is_infinite() {
+                break;
+            }
+            // if high_word(b) != 0xfff00000 {
+            //     break;
+            // }
+            (a, b) = (b, ((2.0 * i as f64) / x) * b - a);
+        }
+        b
+    };
+    if sign > 0 {
+        b
+    } else {
+        -b
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/transcendental/bessel_k.rs.html b/src/ironcalc_base/functions/engineering/transcendental/bessel_k.rs.html new file mode 100644 index 0000000..f6c19aa --- /dev/null +++ b/src/ironcalc_base/functions/engineering/transcendental/bessel_k.rs.html @@ -0,0 +1,181 @@ +bessel_k.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+
// This are somewhat lower precision than the BesselJ and BesselY
+
+use super::bessel_i::bessel_i0;
+use super::bessel_i::bessel_i1;
+
+fn bessel_k0(x: f64) -> f64 {
+    let p1 = -0.57721566;
+    let p2 = 0.42278420;
+    let p3 = 0.23069756;
+    let p4 = 3.488590e-2;
+    let p5 = 2.62698e-3;
+    let p6 = 1.0750e-4;
+    let p7 = 7.4e-6;
+
+    let q1 = 1.25331414;
+    let q2 = -7.832358e-2;
+    let q3 = 2.189568e-2;
+    let q4 = -1.062446e-2;
+    let q5 = 5.87872e-3;
+    let q6 = -2.51540e-3;
+    let q7 = 5.3208e-4;
+
+    if x <= 0.0 {
+        return 0.0;
+    }
+
+    if x <= 2.0 {
+        let y = x * x / 4.0;
+        (-(x / 2.0).ln() * bessel_i0(x))
+            + (p1 + y * (p2 + y * (p3 + y * (p4 + y * (p5 + y * (p6 + y * p7))))))
+    } else {
+        let y = 2.0 / x;
+        ((-x).exp() / x.sqrt())
+            * (q1 + y * (q2 + y * (q3 + y * (q4 + y * (q5 + y * (q6 + y * q7))))))
+    }
+}
+
+fn bessel_k1(x: f64) -> f64 {
+    let p1 = 1.0;
+    let p2 = 0.15443144;
+    let p3 = -0.67278579;
+    let p4 = -0.18156897;
+    let p5 = -1.919402e-2;
+    let p6 = -1.10404e-3;
+    let p7 = -4.686e-5;
+
+    let q1 = 1.25331414;
+    let q2 = 0.23498619;
+    let q3 = -3.655620e-2;
+    let q4 = 1.504268e-2;
+    let q5 = -7.80353e-3;
+    let q6 = 3.25614e-3;
+    let q7 = -6.8245e-4;
+
+    if x <= 0.0 {
+        return f64::NAN;
+    }
+
+    if x <= 2.0 {
+        let y = x * x / 4.0;
+        ((x / 2.0).ln() * bessel_i1(x))
+            + (1. / x) * (p1 + y * (p2 + y * (p3 + y * (p4 + y * (p5 + y * (p6 + y * p7))))))
+    } else {
+        let y = 2.0 / x;
+        ((-x).exp() / x.sqrt())
+            * (q1 + y * (q2 + y * (q3 + y * (q4 + y * (q5 + y * (q6 + y * q7))))))
+    }
+}
+
+pub(crate) fn bessel_k(n: i32, x: f64) -> f64 {
+    if x <= 0.0 || n < 0 {
+        return f64::NAN;
+    }
+
+    if n == 0 {
+        return bessel_k0(x);
+    }
+    if n == 1 {
+        return bessel_k1(x);
+    }
+
+    // Perform upward recurrence for all x
+    let tox = 2.0 / x;
+    let mut bkm = bessel_k0(x);
+    let mut bk = bessel_k1(x);
+    for j in 1..n {
+        (bkm, bk) = (bk, bkm + (j as f64) * tox * bk);
+    }
+    bk
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/transcendental/bessel_util.rs.html b/src/ironcalc_base/functions/engineering/transcendental/bessel_util.rs.html new file mode 100644 index 0000000..5171e13 --- /dev/null +++ b/src/ironcalc_base/functions/engineering/transcendental/bessel_util.rs.html @@ -0,0 +1,39 @@ +bessel_util.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+
pub(crate) const HUGE: f64 = 1e300;
+pub(crate) const FRAC_2_SQRT_PI: f64 = 5.641_895_835_477_563e-1;
+
+pub(crate) fn high_word(x: f64) -> i32 {
+    let [_, _, _, _, a1, a2, a3, a4] = x.to_ne_bytes();
+    // let binding = x.to_ne_bytes();
+    // let high = <&[u8; 4]>::try_from(&binding[4..8]).expect("");
+    i32::from_ne_bytes([a1, a2, a3, a4])
+}
+
+pub(crate) fn split_words(x: f64) -> (i32, i32) {
+    let [a1, a2, a3, a4, b1, b2, b3, b4] = x.to_ne_bytes();
+    // let binding = x.to_ne_bytes();
+    // let high = <&[u8; 4]>::try_from(&binding[4..8]).expect("");
+    (
+        i32::from_ne_bytes([a1, a2, a3, a4]),
+        i32::from_ne_bytes([b1, b2, b3, b4]),
+    )
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/transcendental/erf.rs.html b/src/ironcalc_base/functions/engineering/transcendental/erf.rs.html new file mode 100644 index 0000000..786a434 --- /dev/null +++ b/src/ironcalc_base/functions/engineering/transcendental/erf.rs.html @@ -0,0 +1,107 @@ +erf.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+
pub(crate) fn erf(x: f64) -> f64 {
+    let cof = vec![
+        -1.3026537197817094,
+        6.419_697_923_564_902e-1,
+        1.9476473204185836e-2,
+        -9.561_514_786_808_63e-3,
+        -9.46595344482036e-4,
+        3.66839497852761e-4,
+        4.2523324806907e-5,
+        -2.0278578112534e-5,
+        -1.624290004647e-6,
+        1.303655835580e-6,
+        1.5626441722e-8,
+        -8.5238095915e-8,
+        6.529054439e-9,
+        5.059343495e-9,
+        -9.91364156e-10,
+        -2.27365122e-10,
+        9.6467911e-11,
+        2.394038e-12,
+        -6.886027e-12,
+        8.94487e-13,
+        3.13092e-13,
+        -1.12708e-13,
+        3.81e-16,
+        7.106e-15,
+        -1.523e-15,
+        -9.4e-17,
+        1.21e-16,
+        -2.8e-17,
+    ];
+
+    let mut d = 0.0;
+    let mut dd = 0.0;
+
+    let x_abs = x.abs();
+
+    let t = 2.0 / (2.0 + x_abs);
+    let ty = 4.0 * t - 2.0;
+
+    for j in (1..=cof.len() - 1).rev() {
+        let tmp = d;
+        d = ty * d - dd + cof[j];
+        dd = tmp;
+    }
+
+    let res = t * f64::exp(-x_abs * x_abs + 0.5 * (cof[0] + ty * d) - dd);
+    if x < 0.0 {
+        res - 1.0
+    } else {
+        1.0 - res
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/engineering/transcendental/mod.rs.html b/src/ironcalc_base/functions/engineering/transcendental/mod.rs.html new file mode 100644 index 0000000..348c83f --- /dev/null +++ b/src/ironcalc_base/functions/engineering/transcendental/mod.rs.html @@ -0,0 +1,33 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
mod bessel_i;
+mod bessel_j0_y0;
+mod bessel_j1_y1;
+mod bessel_jn_yn;
+mod bessel_k;
+mod bessel_util;
+mod erf;
+
+#[cfg(test)]
+mod test_bessel;
+
+pub(crate) use bessel_i::bessel_i;
+pub(crate) use bessel_jn_yn::jn as bessel_j;
+pub(crate) use bessel_jn_yn::yn as bessel_y;
+pub(crate) use bessel_k::bessel_k;
+pub(crate) use erf::erf;
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/financial.rs.html b/src/ironcalc_base/functions/financial.rs.html new file mode 100644 index 0000000..8055b40 --- /dev/null +++ b/src/ironcalc_base/functions/financial.rs.html @@ -0,0 +1,3769 @@ +financial.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
+1288
+1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
+1301
+1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
+1316
+1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
+1331
+1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
+1344
+1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
+1355
+1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
+1364
+1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
+1385
+1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
+1406
+1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
+1416
+1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
+1437
+1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
+1458
+1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
+1485
+1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
+1498
+1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
+1529
+1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
+1560
+1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+1574
+1575
+1576
+1577
+1578
+1579
+1580
+1581
+1582
+1583
+1584
+1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
+1599
+1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
+1609
+1610
+1611
+1612
+1613
+1614
+1615
+1616
+1617
+1618
+1619
+1620
+1621
+1622
+1623
+1624
+1625
+1626
+1627
+1628
+1629
+1630
+1631
+1632
+1633
+1634
+1635
+1636
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
+1645
+1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
+1658
+1659
+1660
+1661
+1662
+1663
+1664
+1665
+1666
+1667
+1668
+1669
+1670
+1671
+1672
+1673
+1674
+1675
+1676
+1677
+1678
+1679
+1680
+1681
+1682
+1683
+1684
+1685
+1686
+1687
+1688
+1689
+1690
+1691
+1692
+1693
+1694
+1695
+1696
+1697
+1698
+1699
+1700
+1701
+1702
+1703
+1704
+1705
+1706
+1707
+1708
+1709
+1710
+1711
+1712
+1713
+1714
+1715
+1716
+1717
+1718
+1719
+1720
+1721
+1722
+1723
+1724
+1725
+1726
+1727
+1728
+1729
+1730
+1731
+1732
+1733
+1734
+1735
+1736
+1737
+1738
+1739
+1740
+1741
+1742
+1743
+1744
+1745
+1746
+1747
+1748
+1749
+1750
+1751
+1752
+1753
+1754
+1755
+1756
+1757
+1758
+1759
+1760
+1761
+1762
+1763
+1764
+1765
+1766
+1767
+1768
+1769
+1770
+1771
+1772
+1773
+1774
+1775
+1776
+1777
+1778
+1779
+1780
+1781
+1782
+1783
+1784
+1785
+1786
+1787
+1788
+1789
+1790
+1791
+1792
+1793
+1794
+1795
+1796
+1797
+1798
+1799
+1800
+1801
+1802
+1803
+1804
+1805
+1806
+1807
+1808
+1809
+1810
+1811
+1812
+1813
+1814
+1815
+1816
+1817
+1818
+1819
+1820
+1821
+1822
+1823
+1824
+1825
+1826
+1827
+1828
+1829
+1830
+1831
+1832
+1833
+1834
+1835
+1836
+1837
+1838
+1839
+1840
+1841
+1842
+1843
+1844
+1845
+1846
+1847
+1848
+1849
+1850
+1851
+1852
+1853
+1854
+1855
+1856
+1857
+1858
+1859
+1860
+1861
+1862
+1863
+1864
+1865
+1866
+1867
+1868
+1869
+1870
+1871
+1872
+1873
+1874
+1875
+1876
+1877
+1878
+1879
+1880
+1881
+1882
+1883
+1884
+
use chrono::Datelike;
+
+use crate::{
+    calc_result::{CalcResult, CellReference},
+    constants::{LAST_COLUMN, LAST_ROW},
+    expressions::{parser::Node, token::Error},
+    formatter::dates::from_excel_date,
+    model::Model,
+};
+
+use super::financial_util::{compute_irr, compute_npv, compute_rate, compute_xirr, compute_xnpv};
+
+// See:
+// https://github.com/apache/openoffice/blob/c014b5f2b55cff8d4b0c952d5c16d62ecde09ca1/main/scaddins/source/analysis/financial.cxx
+
+// FIXME: Is this enough?
+fn is_valid_date(date: f64) -> bool {
+    date > 0.0
+}
+
+fn is_less_than_one_year(start_date: i64, end_date: i64) -> bool {
+    if end_date - start_date < 365 {
+        return true;
+    }
+    let end = from_excel_date(end_date);
+    let start = from_excel_date(start_date);
+    let end_year = end.year();
+    let start_year = start.year();
+    if end_year == start_year {
+        return true;
+    }
+    if end_year != start_year + 1 {
+        return false;
+    }
+    let start_month = start.month();
+    let end_month = end.month();
+    if end_month < start_month {
+        return true;
+    }
+    if end_month > start_month {
+        return false;
+    }
+    // we are one year later same month
+    let start_day = start.day();
+    let end_day = end.day();
+    end_day <= start_day
+}
+
+fn compute_payment(
+    rate: f64,
+    nper: f64,
+    pv: f64,
+    fv: f64,
+    period_start: bool,
+) -> Result<f64, (Error, String)> {
+    if rate == 0.0 {
+        if nper == 0.0 {
+            return Err((Error::NUM, "Period count must be non zero".to_string()));
+        }
+        return Ok(-(pv + fv) / nper);
+    }
+    if rate <= -1.0 {
+        return Err((Error::NUM, "Rate must be > -1".to_string()));
+    };
+    let rate_nper = if nper == 0.0 {
+        1.0
+    } else {
+        (1.0 + rate).powf(nper)
+    };
+    let result = if period_start {
+        // type = 1
+        (fv + pv * rate_nper) * rate / ((1.0 + rate) * (1.0 - rate_nper))
+    } else {
+        (fv * rate + pv * rate * rate_nper) / (1.0 - rate_nper)
+    };
+    if result.is_nan() || result.is_infinite() {
+        return Err((Error::NUM, "Invalid result".to_string()));
+    }
+    Ok(result)
+}
+
+fn compute_future_value(
+    rate: f64,
+    nper: f64,
+    pmt: f64,
+    pv: f64,
+    period_start: bool,
+) -> Result<f64, (Error, String)> {
+    if rate == 0.0 {
+        return Ok(-pv - pmt * nper);
+    }
+
+    let rate_nper = (1.0 + rate).powf(nper);
+    let fv = if period_start {
+        // type = 1
+        -pv * rate_nper - pmt * (1.0 + rate) * (rate_nper - 1.0) / rate
+    } else {
+        -pv * rate_nper - pmt * (rate_nper - 1.0) / rate
+    };
+    if fv.is_nan() {
+        return Err((Error::NUM, "Invalid result".to_string()));
+    }
+    if !fv.is_finite() {
+        return Err((Error::DIV, "Divide by zero".to_string()));
+    }
+    Ok(fv)
+}
+
+fn compute_ipmt(
+    rate: f64,
+    period: f64,
+    period_count: f64,
+    present_value: f64,
+    future_value: f64,
+    period_start: bool,
+) -> Result<f64, (Error, String)> {
+    // http://www.staff.city.ac.uk/o.s.kerr/CompMaths/WSheet4.pdf
+    // https://www.experts-exchange.com/articles/1948/A-Guide-to-the-PMT-FV-IPMT-and-PPMT-Functions.html
+    // type = 0 (end of period)
+    // impt = -[(1+rate)^(period-1)*(pv*rate+pmt)-pmt]
+    // ipmt = FV(rate, period-1, payment, pv, type) * rate
+    // type = 1 (beginning of period)
+    // ipmt = (FV(rate, period-2, payment, pv, type) - payment) * rate
+    let payment = compute_payment(
+        rate,
+        period_count,
+        present_value,
+        future_value,
+        period_start,
+    )?;
+    if period < 1.0 || period >= period_count + 1.0 {
+        return Err((
+            Error::NUM,
+            format!("Period must be between 1 and {}", period_count + 1.0),
+        ));
+    }
+    if period == 1.0 && period_start {
+        Ok(0.0)
+    } else {
+        let p = if period_start {
+            period - 2.0
+        } else {
+            period - 1.0
+        };
+        let c = if period_start { -payment } else { 0.0 };
+        let fv = compute_future_value(rate, p, payment, present_value, period_start)?;
+        Ok((fv + c) * rate)
+    }
+}
+
+fn compute_ppmt(
+    rate: f64,
+    period: f64,
+    period_count: f64,
+    present_value: f64,
+    future_value: f64,
+    period_start: bool,
+) -> Result<f64, (Error, String)> {
+    let payment = compute_payment(
+        rate,
+        period_count,
+        present_value,
+        future_value,
+        period_start,
+    )?;
+    // It's a bit unfortunate that the first thing compute_ipmt does is compute_payment again
+    let ipmt = compute_ipmt(
+        rate,
+        period,
+        period_count,
+        present_value,
+        future_value,
+        period_start,
+    )?;
+    Ok(payment - ipmt)
+}
+
+// These formulas revolve around compound interest and annuities.
+// The financial functions pv, rate, nper, pmt and fv:
+// rate = interest rate per period
+// nper (number of periods) = loan term
+// pv (present value) = loan amount
+// fv (future value) = cash balance after last payment. Default is 0
+// type = the annuity type indicates when payments are due
+//         * 0 (default) Payments are made at the end of the period
+//         * 1 Payments are made at the beginning of the period (like a lease or rent)
+// The variable period_start is true if type is 1
+// They are linked by the formulas:
+// If rate != 0
+//   $pv*(1+rate)^nper+pmt*(1+rate*type)*((1+rate)^nper-1)/rate+fv=0$
+// If rate = 0
+//   $pmt*nper+pv+fv=0$
+// All, except for rate are easily solvable in terms of the others.
+// In these formulas the payment (pmt) is normally negative
+
+impl Model {
+    // FIXME: These three functions (get_array_of_numbers..) need to be refactored
+    // They are really similar expect for small issues
+    fn get_array_of_numbers(
+        &mut self,
+        arg: &Node,
+        cell: &CellReference,
+    ) -> Result<Vec<f64>, CalcResult> {
+        let mut values = Vec::new();
+        match self.evaluate_node_in_context(arg, *cell) {
+            CalcResult::Number(value) => values.push(value),
+            CalcResult::Range { left, right } => {
+                if left.sheet != right.sheet {
+                    return Err(CalcResult::new_error(
+                        Error::VALUE,
+                        *cell,
+                        "Ranges are in different sheets".to_string(),
+                    ));
+                }
+                let row1 = left.row;
+                let mut row2 = right.row;
+                let column1 = left.column;
+                let mut column2 = right.column;
+                if row1 == 1 && row2 == LAST_ROW {
+                    row2 = self
+                        .workbook
+                        .worksheet(left.sheet)
+                        .expect("Sheet expected during evaluation.")
+                        .dimension()
+                        .max_row;
+                }
+                if column1 == 1 && column2 == LAST_COLUMN {
+                    column2 = self
+                        .workbook
+                        .worksheet(left.sheet)
+                        .expect("Sheet expected during evaluation.")
+                        .dimension()
+                        .max_column;
+                }
+                for row in row1..row2 + 1 {
+                    for column in column1..(column2 + 1) {
+                        match self.evaluate_cell(CellReference {
+                            sheet: left.sheet,
+                            row,
+                            column,
+                        }) {
+                            CalcResult::Number(value) => {
+                                values.push(value);
+                            }
+                            error @ CalcResult::Error { .. } => return Err(error),
+                            _ => {
+                                // We ignore booleans and strings
+                            }
+                        }
+                    }
+                }
+            }
+            error @ CalcResult::Error { .. } => return Err(error),
+            _ => {
+                // We ignore booleans and strings
+            }
+        };
+        Ok(values)
+    }
+
+    fn get_array_of_numbers_xpnv(
+        &mut self,
+        arg: &Node,
+        cell: &CellReference,
+        error: Error,
+    ) -> Result<Vec<f64>, CalcResult> {
+        let mut values = Vec::new();
+        match self.evaluate_node_in_context(arg, *cell) {
+            CalcResult::Number(value) => values.push(value),
+            CalcResult::Range { left, right } => {
+                if left.sheet != right.sheet {
+                    return Err(CalcResult::new_error(
+                        Error::VALUE,
+                        *cell,
+                        "Ranges are in different sheets".to_string(),
+                    ));
+                }
+                let row1 = left.row;
+                let mut row2 = right.row;
+                let column1 = left.column;
+                let mut column2 = right.column;
+                if row1 == 1 && row2 == LAST_ROW {
+                    row2 = self
+                        .workbook
+                        .worksheet(left.sheet)
+                        .expect("Sheet expected during evaluation.")
+                        .dimension()
+                        .max_row;
+                }
+                if column1 == 1 && column2 == LAST_COLUMN {
+                    column2 = self
+                        .workbook
+                        .worksheet(left.sheet)
+                        .expect("Sheet expected during evaluation.")
+                        .dimension()
+                        .max_column;
+                }
+                for row in row1..row2 + 1 {
+                    for column in column1..(column2 + 1) {
+                        match self.evaluate_cell(CellReference {
+                            sheet: left.sheet,
+                            row,
+                            column,
+                        }) {
+                            CalcResult::Number(value) => {
+                                values.push(value);
+                            }
+                            error @ CalcResult::Error { .. } => return Err(error),
+                            CalcResult::EmptyCell => {
+                                return Err(CalcResult::new_error(
+                                    Error::NUM,
+                                    *cell,
+                                    "Expected number".to_string(),
+                                ));
+                            }
+                            _ => {
+                                return Err(CalcResult::new_error(
+                                    error,
+                                    *cell,
+                                    "Expected number".to_string(),
+                                ));
+                            }
+                        }
+                    }
+                }
+            }
+            error @ CalcResult::Error { .. } => return Err(error),
+            _ => {
+                return Err(CalcResult::new_error(
+                    error,
+                    *cell,
+                    "Expected number".to_string(),
+                ));
+            }
+        };
+        Ok(values)
+    }
+
+    fn get_array_of_numbers_xirr(
+        &mut self,
+        arg: &Node,
+        cell: &CellReference,
+    ) -> Result<Vec<f64>, CalcResult> {
+        let mut values = Vec::new();
+        match self.evaluate_node_in_context(arg, *cell) {
+            CalcResult::Range { left, right } => {
+                if left.sheet != right.sheet {
+                    return Err(CalcResult::new_error(
+                        Error::VALUE,
+                        *cell,
+                        "Ranges are in different sheets".to_string(),
+                    ));
+                }
+                let row1 = left.row;
+                let mut row2 = right.row;
+                let column1 = left.column;
+                let mut column2 = right.column;
+                if row1 == 1 && row2 == LAST_ROW {
+                    row2 = self
+                        .workbook
+                        .worksheet(left.sheet)
+                        .expect("Sheet expected during evaluation.")
+                        .dimension()
+                        .max_row;
+                }
+                if column1 == 1 && column2 == LAST_COLUMN {
+                    column2 = self
+                        .workbook
+                        .worksheet(left.sheet)
+                        .expect("Sheet expected during evaluation.")
+                        .dimension()
+                        .max_column;
+                }
+                for row in row1..row2 + 1 {
+                    for column in column1..(column2 + 1) {
+                        match self.evaluate_cell(CellReference {
+                            sheet: left.sheet,
+                            row,
+                            column,
+                        }) {
+                            CalcResult::Number(value) => {
+                                values.push(value);
+                            }
+                            error @ CalcResult::Error { .. } => return Err(error),
+                            CalcResult::EmptyCell => values.push(0.0),
+                            _ => {
+                                return Err(CalcResult::new_error(
+                                    Error::VALUE,
+                                    *cell,
+                                    "Expected number".to_string(),
+                                ));
+                            }
+                        }
+                    }
+                }
+            }
+            error @ CalcResult::Error { .. } => return Err(error),
+            _ => {
+                return Err(CalcResult::new_error(
+                    Error::VALUE,
+                    *cell,
+                    "Expected number".to_string(),
+                ));
+            }
+        };
+        Ok(values)
+    }
+
+    /// PMT(rate, nper, pv, [fv], [type])
+    pub(crate) fn fn_pmt(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(3..=5).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let rate = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // number of periods
+        let nper = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // present value
+        let pv = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // future_value
+        let fv = if arg_count > 3 {
+            match self.get_number(&args[3], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            0.0
+        };
+        let period_start = if arg_count > 4 {
+            match self.get_number(&args[4], cell) {
+                Ok(f) => f != 0.0,
+                Err(s) => return s,
+            }
+        } else {
+            // at the end of the period
+            false
+        };
+        match compute_payment(rate, nper, pv, fv, period_start) {
+            Ok(p) => CalcResult::Number(p),
+            Err(error) => CalcResult::Error {
+                error: error.0,
+                origin: cell,
+                message: error.1,
+            },
+        }
+    }
+
+    // PV(rate, nper, pmt, [fv], [type])
+    pub(crate) fn fn_pv(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(3..=5).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let rate = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // nper
+        let period_count = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // pmt
+        let payment = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // fv
+        let future_value = if arg_count > 3 {
+            match self.get_number(&args[3], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            0.0
+        };
+        let period_start = if arg_count > 4 {
+            match self.get_number(&args[4], cell) {
+                Ok(f) => f != 0.0,
+                Err(s) => return s,
+            }
+        } else {
+            // at the end of the period
+            false
+        };
+        if rate == 0.0 {
+            return CalcResult::Number(-future_value - payment * period_count);
+        }
+        if rate == -1.0 {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Rate must be != -1".to_string(),
+            };
+        };
+        let rate_nper = (1.0 + rate).powf(period_count);
+        let result = if period_start {
+            // type = 1
+            -(future_value * rate + payment * (1.0 + rate) * (rate_nper - 1.0)) / (rate * rate_nper)
+        } else {
+            (-future_value * rate - payment * (rate_nper - 1.0)) / (rate * rate_nper)
+        };
+        if result.is_nan() || result.is_infinite() {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid result".to_string(),
+            };
+        }
+
+        CalcResult::Number(result)
+    }
+
+    // RATE(nper, pmt, pv, [fv], [type], [guess])
+    pub(crate) fn fn_rate(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(3..=5).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let nper = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let pmt = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let pv = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // fv
+        let fv = if arg_count > 3 {
+            match self.get_number(&args[3], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            0.0
+        };
+        let annuity_type = if arg_count > 4 {
+            match self.get_number(&args[4], cell) {
+                Ok(f) => i32::from(f != 0.0),
+                Err(s) => return s,
+            }
+        } else {
+            // at the end of the period
+            0
+        };
+
+        let guess = if arg_count > 5 {
+            match self.get_number(&args[5], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            0.1
+        };
+
+        match compute_rate(pv, fv, nper, pmt, annuity_type, guess) {
+            Ok(f) => CalcResult::Number(f),
+            Err(error) => CalcResult::Error {
+                error: error.0,
+                origin: cell,
+                message: error.1,
+            },
+        }
+    }
+
+    // NPER(rate,pmt,pv,[fv],[type])
+    pub(crate) fn fn_nper(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(3..=5).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let rate = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // pmt
+        let payment = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // pv
+        let present_value = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // fv
+        let future_value = if arg_count > 3 {
+            match self.get_number(&args[3], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            0.0
+        };
+        let period_start = if arg_count > 4 {
+            match self.get_number(&args[4], cell) {
+                Ok(f) => f != 0.0,
+                Err(s) => return s,
+            }
+        } else {
+            // at the end of the period
+            false
+        };
+        if rate == 0.0 {
+            if payment == 0.0 {
+                return CalcResult::Error {
+                    error: Error::DIV,
+                    origin: cell,
+                    message: "Divide by zero".to_string(),
+                };
+            }
+            return CalcResult::Number(-(future_value + present_value) / payment);
+        }
+        if rate < -1.0 {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Rate must be > -1".to_string(),
+            };
+        };
+        let rate_nper = if period_start {
+            // type = 1
+            if payment != 0.0 {
+                let term = payment * (1.0 + rate) / rate;
+                (1.0 - future_value / term) / (1.0 + present_value / term)
+            } else {
+                -future_value / present_value
+            }
+        } else {
+            // type = 0
+            if payment != 0.0 {
+                let term = payment / rate;
+                (1.0 - future_value / term) / (1.0 + present_value / term)
+            } else {
+                -future_value / present_value
+            }
+        };
+        if rate_nper <= 0.0 {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Cannot compute.".to_string(),
+            };
+        }
+        let result = rate_nper.ln() / (1.0 + rate).ln();
+        CalcResult::Number(result)
+    }
+
+    // FV(rate, nper, pmt, [pv], [type])
+    pub(crate) fn fn_fv(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(3..=5).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let rate = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // number of periods
+        let nper = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // payment
+        let pmt = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // present value
+        let pv = if arg_count > 3 {
+            match self.get_number(&args[3], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            0.0
+        };
+        let period_start = if arg_count > 4 {
+            match self.get_number(&args[4], cell) {
+                Ok(f) => f != 0.0,
+                Err(s) => return s,
+            }
+        } else {
+            // at the end of the period
+            false
+        };
+        match compute_future_value(rate, nper, pmt, pv, period_start) {
+            Ok(f) => CalcResult::Number(f),
+            Err(error) => CalcResult::Error {
+                error: error.0,
+                origin: cell,
+                message: error.1,
+            },
+        }
+    }
+
+    // IPMT(rate, per, nper, pv, [fv], [type])
+    pub(crate) fn fn_ipmt(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(4..=6).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let rate = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // per
+        let period = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // nper
+        let period_count = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // pv
+        let present_value = match self.get_number(&args[3], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // fv
+        let future_value = if arg_count > 4 {
+            match self.get_number(&args[4], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            0.0
+        };
+        let period_start = if arg_count > 5 {
+            match self.get_number(&args[5], cell) {
+                Ok(f) => f != 0.0,
+                Err(s) => return s,
+            }
+        } else {
+            // at the end of the period
+            false
+        };
+        let ipmt = match compute_ipmt(
+            rate,
+            period,
+            period_count,
+            present_value,
+            future_value,
+            period_start,
+        ) {
+            Ok(f) => f,
+            Err(error) => {
+                return CalcResult::Error {
+                    error: error.0,
+                    origin: cell,
+                    message: error.1,
+                }
+            }
+        };
+        CalcResult::Number(ipmt)
+    }
+
+    // PPMT(rate, per, nper, pv, [fv], [type])
+    pub(crate) fn fn_ppmt(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(4..=6).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let rate = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // per
+        let period = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // nper
+        let period_count = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // pv
+        let present_value = match self.get_number(&args[3], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // fv
+        let future_value = if arg_count > 4 {
+            match self.get_number(&args[4], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            0.0
+        };
+        let period_start = if arg_count > 5 {
+            match self.get_number(&args[5], cell) {
+                Ok(f) => f != 0.0,
+                Err(s) => return s,
+            }
+        } else {
+            // at the end of the period
+            false
+        };
+
+        let ppmt = match compute_ppmt(
+            rate,
+            period,
+            period_count,
+            present_value,
+            future_value,
+            period_start,
+        ) {
+            Ok(f) => f,
+            Err(error) => {
+                return CalcResult::Error {
+                    error: error.0,
+                    origin: cell,
+                    message: error.1,
+                }
+            }
+        };
+        CalcResult::Number(ppmt)
+    }
+
+    // NPV(rate, value1, [value2],...)
+    // npv = Sum[value[i]/(1+rate)^i, {i, 1, n}]
+    pub(crate) fn fn_npv(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if arg_count < 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let rate = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let mut values = Vec::new();
+        for arg in &args[1..] {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::Number(value) => values.push(value),
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    let row1 = left.row;
+                    let mut row2 = right.row;
+                    let column1 = left.column;
+                    let mut column2 = right.column;
+                    if row1 == 1 && row2 == LAST_ROW {
+                        row2 = self
+                            .workbook
+                            .worksheet(left.sheet)
+                            .expect("Sheet expected during evaluation.")
+                            .dimension()
+                            .max_row;
+                    }
+                    if column1 == 1 && column2 == LAST_COLUMN {
+                        column2 = self
+                            .workbook
+                            .worksheet(left.sheet)
+                            .expect("Sheet expected during evaluation.")
+                            .dimension()
+                            .max_column;
+                    }
+                    for row in row1..row2 + 1 {
+                        for column in column1..(column2 + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::Number(value) => {
+                                    values.push(value);
+                                }
+                                error @ CalcResult::Error { .. } => return error,
+                                _ => {
+                                    // We ignore booleans and strings
+                                }
+                            }
+                        }
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                _ => {
+                    // We ignore booleans and strings
+                }
+            };
+        }
+        match compute_npv(rate, &values) {
+            Ok(f) => CalcResult::Number(f),
+            Err(error) => CalcResult::new_error(error.0, cell, error.1),
+        }
+    }
+
+    // Returns the internal rate of return for a series of cash flows represented by the numbers
+    // in values.
+    // These cash flows do not have to be even, as they would be for an annuity.
+    // However, the cash flows must occur at regular intervals, such as monthly or annually.
+    // The internal rate of return is the interest rate received for an investment consisting
+    // of payments (negative values) and income (positive values) that occur at regular periods
+
+    // IRR(values, [guess])
+    pub(crate) fn fn_irr(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if arg_count > 2 || arg_count == 0 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let values = match self.get_array_of_numbers(&args[0], &cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let guess = if arg_count == 2 {
+            match self.get_number(&args[1], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            0.1
+        };
+        match compute_irr(&values, guess) {
+            Ok(f) => CalcResult::Number(f),
+            Err(error) => CalcResult::Error {
+                error: error.0,
+                origin: cell,
+                message: error.1,
+            },
+        }
+    }
+
+    // XNPV(rate, values, dates)
+    pub(crate) fn fn_xnpv(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(2..=3).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let rate = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let values = match self.get_array_of_numbers_xpnv(&args[1], &cell, Error::NUM) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let dates = match self.get_array_of_numbers_xpnv(&args[2], &cell, Error::VALUE) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        // Decimal points on dates are truncated
+        let dates: Vec<f64> = dates.iter().map(|s| s.floor()).collect();
+        let values_count = values.len();
+        // If values and dates contain a different number of values, XNPV returns the #NUM! error value.
+        if values_count != dates.len() {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "Values and dates must be the same length".to_string(),
+            );
+        }
+        if values_count == 0 {
+            return CalcResult::new_error(Error::NUM, cell, "Not enough values".to_string());
+        }
+        let first_date = dates[0];
+        for date in &dates {
+            if !is_valid_date(*date) {
+                // Excel docs claim that if any number in dates is not a valid date,
+                // XNPV returns the #VALUE! error value, but it seems to return #VALUE!
+                return CalcResult::new_error(
+                    Error::NUM,
+                    cell,
+                    "Invalid number for date".to_string(),
+                );
+            }
+            // If any number in dates precedes the starting date, XNPV returns the #NUM! error value.
+            if date < &first_date {
+                return CalcResult::new_error(
+                    Error::NUM,
+                    cell,
+                    "Date precedes the starting date".to_string(),
+                );
+            }
+        }
+        // It seems Excel returns #NUM! if rate < 0, this is only necessary if r <= -1
+        if rate <= 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "rate needs to be > 0".to_string());
+        }
+        match compute_xnpv(rate, &values, &dates) {
+            Ok(f) => CalcResult::Number(f),
+            Err((error, message)) => CalcResult::new_error(error, cell, message),
+        }
+    }
+
+    // XIRR(values, dates, [guess])
+    pub(crate) fn fn_xirr(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(2..=3).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let values = match self.get_array_of_numbers_xirr(&args[0], &cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let dates = match self.get_array_of_numbers_xirr(&args[1], &cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let guess = if arg_count == 3 {
+            match self.get_number(&args[2], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            0.1
+        };
+        // Decimal points on dates are truncated
+        let dates: Vec<f64> = dates.iter().map(|s| s.floor()).collect();
+        let values_count = values.len();
+        // If values and dates contain a different number of values, XNPV returns the #NUM! error value.
+        if values_count != dates.len() {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "Values and dates must be the same length".to_string(),
+            );
+        }
+        if values_count == 0 {
+            return CalcResult::new_error(Error::NUM, cell, "Not enough values".to_string());
+        }
+        let first_date = dates[0];
+        for date in &dates {
+            if !is_valid_date(*date) {
+                return CalcResult::new_error(
+                    Error::NUM,
+                    cell,
+                    "Invalid number for date".to_string(),
+                );
+            }
+            // If any number in dates precedes the starting date, XIRR returns the #NUM! error value.
+            if date < &first_date {
+                return CalcResult::new_error(
+                    Error::NUM,
+                    cell,
+                    "Date precedes the starting date".to_string(),
+                );
+            }
+        }
+        match compute_xirr(&values, &dates, guess) {
+            Ok(f) => CalcResult::Number(f),
+            Err((error, message)) => CalcResult::Error {
+                error,
+                origin: cell,
+                message,
+            },
+        }
+    }
+
+    //  MIRR(values, finance_rate, reinvest_rate)
+    // The formula is:
+    // $$ (-NPV(r1, v_p) * (1+r1)^y)/(NPV(r2, v_n)*(1+r2))^(1/y)-1$$
+    // where:
+    // $r1$ is the reinvest_rate, $r2$ the finance_rate
+    // $v_p$ the vector of positive values
+    // $v_n$ the vector of negative values
+    // and $y$ is dimension of $v$ - 1 (number of years)
+    pub(crate) fn fn_mirr(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let values = match self.get_array_of_numbers(&args[0], &cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let finance_rate = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let reinvest_rate = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let mut positive_values = Vec::new();
+        let mut negative_values = Vec::new();
+        let mut last_negative_index = -1;
+        for (index, &value) in values.iter().enumerate() {
+            let (p, n) = if value >= 0.0 {
+                (value, 0.0)
+            } else {
+                last_negative_index = index as i32;
+                (0.0, value)
+            };
+            positive_values.push(p);
+            negative_values.push(n);
+        }
+        if last_negative_index == -1 {
+            return CalcResult::new_error(
+                Error::DIV,
+                cell,
+                "Invalid data for MIRR function".to_string(),
+            );
+        }
+        // We do a bit of analysis if the rates are -1 as there are some cancellations
+        // It is probably not important.
+        let years = values.len() as f64;
+        let top = if reinvest_rate == -1.0 {
+            // This is finite
+            match positive_values.last() {
+                Some(f) => *f,
+                None => 0.0,
+            }
+        } else {
+            match compute_npv(reinvest_rate, &positive_values) {
+                Ok(npv) => -npv * ((1.0 + reinvest_rate).powf(years)),
+                Err((error, message)) => {
+                    return CalcResult::Error {
+                        error,
+                        origin: cell,
+                        message,
+                    }
+                }
+            }
+        };
+        let bottom = if finance_rate == -1.0 {
+            if last_negative_index == 0 {
+                // This is still finite
+                negative_values[last_negative_index as usize]
+            } else {
+                // or -Infinity depending of the sign in the last_negative_index coef.
+                // But it is irrelevant for the calculation
+                f64::INFINITY
+            }
+        } else {
+            match compute_npv(finance_rate, &negative_values) {
+                Ok(npv) => npv * (1.0 + finance_rate),
+                Err((error, message)) => {
+                    return CalcResult::Error {
+                        error,
+                        origin: cell,
+                        message,
+                    }
+                }
+            }
+        };
+
+        let result = (top / bottom).powf(1.0 / (years - 1.0)) - 1.0;
+        if result.is_infinite() {
+            return CalcResult::new_error(Error::DIV, cell, "Division by 0".to_string());
+        }
+        if result.is_nan() {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid data for MIRR".to_string());
+        }
+        CalcResult::Number(result)
+    }
+
+    // ISPMT(rate, per, nper, pv)
+    // Formula is:
+    // $$pv*rate*\left(\frac{per}{nper}-1\right)$$
+    pub(crate) fn fn_ispmt(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 4 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let rate = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let per = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let nper = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let pv = match self.get_number(&args[3], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if nper == 0.0 {
+            return CalcResult::new_error(Error::DIV, cell, "Division by 0".to_string());
+        }
+        CalcResult::Number(pv * rate * (per / nper - 1.0))
+    }
+
+    // RRI(nper, pv, fv)
+    // Formula is
+    // $$ \left(\frac{fv}{pv}\right)^{\frac{1}{nper}}-1  $$
+    pub(crate) fn fn_rri(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let nper = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let pv = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let fv = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if nper <= 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "nper should be >0".to_string());
+        }
+        if pv == 0.0 {
+            // Note error is NUM not DIV/0 also bellow
+            return CalcResult::new_error(Error::NUM, cell, "Division by 0".to_string());
+        }
+        let result = (fv / pv).powf(1.0 / nper) - 1.0;
+        if result.is_infinite() {
+            return CalcResult::new_error(Error::NUM, cell, "Division by 0".to_string());
+        }
+        if result.is_nan() {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid data for RRI".to_string());
+        }
+
+        CalcResult::Number(result)
+    }
+
+    // SLN(cost, salvage, life)
+    // Formula is:
+    // $$ \frac{cost-salvage}{life} $$
+    pub(crate) fn fn_sln(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let cost = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let salvage = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let life = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if life == 0.0 {
+            return CalcResult::new_error(Error::DIV, cell, "Division by 0".to_string());
+        }
+        let result = (cost - salvage) / life;
+
+        CalcResult::Number(result)
+    }
+
+    // SYD(cost, salvage, life, per)
+    // Formula is:
+    // $$ \frac{(cost-salvage)*(life-per+1)*2}{life*(life+1)} $$
+    pub(crate) fn fn_syd(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 4 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let cost = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let salvage = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let life = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let per = match self.get_number(&args[3], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if life == 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "Division by 0".to_string());
+        }
+        if per > life || per <= 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "per should be <= life".to_string());
+        }
+        let result = ((cost - salvage) * (life - per + 1.0) * 2.0) / (life * (life + 1.0));
+
+        CalcResult::Number(result)
+    }
+
+    // NOMINAL(effective_rate, npery)
+    // Formula is:
+    // $$ n\times\left(\left(1+r\right)^{\frac{1}{n}}-1\right) $$
+    // where:
+    //   $r$ is the effective interest rate
+    //   $n$ is the number of periods per year
+    pub(crate) fn fn_nominal(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let effect_rate = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let npery = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f.floor(),
+            Err(s) => return s,
+        };
+        if effect_rate <= 0.0 || npery < 1.0 {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid arguments".to_string());
+        }
+        let result = ((1.0 + effect_rate).powf(1.0 / npery) - 1.0) * npery;
+        if result.is_infinite() {
+            return CalcResult::new_error(Error::DIV, cell, "Division by 0".to_string());
+        }
+        if result.is_nan() {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid data for RRI".to_string());
+        }
+
+        CalcResult::Number(result)
+    }
+
+    // EFFECT(nominal_rate, npery)
+    // Formula is:
+    // $$ \left(1+\frac{r}{n}\right)^n-1 $$
+    // where:
+    //   $r$ is the nominal interest rate
+    //   $n$ is the number of periods per year
+    pub(crate) fn fn_effect(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let nominal_rate = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let npery = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f.floor(),
+            Err(s) => return s,
+        };
+        if nominal_rate <= 0.0 || npery < 1.0 {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid arguments".to_string());
+        }
+        let result = (1.0 + nominal_rate / npery).powf(npery) - 1.0;
+        if result.is_infinite() {
+            return CalcResult::new_error(Error::DIV, cell, "Division by 0".to_string());
+        }
+        if result.is_nan() {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid data for RRI".to_string());
+        }
+
+        CalcResult::Number(result)
+    }
+
+    // PDURATION(rate, pv, fv)
+    // Formula is:
+    // $$ \frac{log(fv) - log(pv)}{log(1+r)} $$
+    // where:
+    //   * $r$ is the interest rate per period
+    //   * $pv$ is the present value of the investment
+    //   * $fv$ is the desired future value of the investment
+    pub(crate) fn fn_pduration(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let rate = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let pv = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let fv = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if fv <= 0.0 || pv <= 0.0 || rate <= 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid arguments".to_string());
+        }
+        let result = (fv.ln() - pv.ln()) / ((1.0 + rate).ln());
+        if result.is_infinite() {
+            return CalcResult::new_error(Error::DIV, cell, "Division by 0".to_string());
+        }
+        if result.is_nan() {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid data for RRI".to_string());
+        }
+
+        CalcResult::Number(result)
+    }
+
+    /// This next three functions deal with Treasure Bills or T-Bills for short
+    /// They are zero-coupon that mature in one year or less.
+    ///  Definitions:
+    ///    $r$ be the discount rate
+    ///    $v$ the face value of the Bill
+    ///    $p$ the price of the Bill
+    ///    $d_m$ is the number of days from the settlement to maturity
+    /// Then:
+    ///   $$ p = v \times\left(1-\frac{d_m}{r}\right) $$
+    /// If d_m is less than 183 days the he Bond Equivalent Yield (BEY, here $y$) is given by:
+    /// $$ y = \frac{F - B}{M}\times \frac{365}{d_m} = \frac{365\times r}{360-r\times d_m}
+    /// If d_m>= 183 days things are a bit more complicated.
+    /// Let $d_e = d_m - 365/2$ if $d_m <= 365$ or $d_e = 183$ if $d_m = 366$.
+    /// $$ v = p\times \left(1+\frac{y}{2}\right)\left(1+d_e\times\frac{y}{365}\right) $$
+    /// Together with the previous relation of $p$ and $v$ gives us a quadratic equation for $y$.
+
+    // TBILLEQ(settlement, maturity, discount)
+    pub(crate) fn fn_tbilleq(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let settlement = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let maturity = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let discount = match self.get_number_no_bools(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if !is_valid_date(settlement) || !is_valid_date(maturity) {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid date".to_string());
+        }
+        if settlement > maturity {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "settlement should be <= maturity".to_string(),
+            );
+        }
+        if !is_less_than_one_year(settlement as i64, maturity as i64) {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "maturity <= settlement + year".to_string(),
+            );
+        }
+        if discount <= 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "discount should be >0".to_string());
+        }
+        // days to maturity
+        let d_m = maturity - settlement;
+        let result = if d_m < 183.0 {
+            365.0 * discount / (360.0 - discount * d_m)
+        } else {
+            // Equation here is:
+            // (1-days*rate/360)*(1+y/2)*(1+d_extra*y/year)=1
+            let year = if d_m == 366.0 { 366.0 } else { 365.0 };
+            let d_extra = d_m - year / 2.0;
+            let alpha = 1.0 - d_m * discount / 360.0;
+            let beta = 0.5 + d_extra / year;
+            // ay^2+by+c=0
+            let a = d_extra * alpha / (year * 2.0);
+            let b = alpha * beta;
+            let c = alpha - 1.0;
+            (-b + (b * b - 4.0 * a * c).sqrt()) / (2.0 * a)
+        };
+        if result.is_infinite() {
+            return CalcResult::new_error(Error::DIV, cell, "Division by 0".to_string());
+        }
+        if result.is_nan() {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid data for RRI".to_string());
+        }
+
+        CalcResult::Number(result)
+    }
+
+    // TBILLPRICE(settlement, maturity, discount)
+    pub(crate) fn fn_tbillprice(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let settlement = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let maturity = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let discount = match self.get_number_no_bools(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if !is_valid_date(settlement) || !is_valid_date(maturity) {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid date".to_string());
+        }
+        if settlement > maturity {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "settlement should be <= maturity".to_string(),
+            );
+        }
+        if !is_less_than_one_year(settlement as i64, maturity as i64) {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "maturity <= settlement + year".to_string(),
+            );
+        }
+        if discount <= 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "discount should be >0".to_string());
+        }
+        // days to maturity
+        let d_m = maturity - settlement;
+        let result = 100.0 * (1.0 - discount * d_m / 360.0);
+        if result.is_infinite() {
+            return CalcResult::new_error(Error::DIV, cell, "Division by 0".to_string());
+        }
+        if result.is_nan() || result < 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid data for RRI".to_string());
+        }
+
+        CalcResult::Number(result)
+    }
+
+    // TBILLYIELD(settlement, maturity, pr)
+    pub(crate) fn fn_tbillyield(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let settlement = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let maturity = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let pr = match self.get_number_no_bools(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if !is_valid_date(settlement) || !is_valid_date(maturity) {
+            return CalcResult::new_error(Error::NUM, cell, "Invalid date".to_string());
+        }
+        if settlement > maturity {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "settlement should be <= maturity".to_string(),
+            );
+        }
+        if !is_less_than_one_year(settlement as i64, maturity as i64) {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "maturity <= settlement + year".to_string(),
+            );
+        }
+        if pr <= 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "discount should be >0".to_string());
+        }
+        let days = maturity - settlement;
+        let result = (100.0 - pr) * 360.0 / (pr * days);
+
+        CalcResult::Number(result)
+    }
+
+    // DOLLARDE(fractional_dollar, fraction)
+    pub(crate) fn fn_dollarde(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let fractional_dollar = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let mut fraction = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if fraction < 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "fraction should be >= 1".to_string());
+        }
+        if fraction < 1.0 {
+            // this is not necessarily DIV/0
+            return CalcResult::new_error(Error::DIV, cell, "fraction should be >= 1".to_string());
+        }
+        fraction = fraction.trunc();
+        while fraction > 10.0 {
+            fraction /= 10.0;
+        }
+        let t = fractional_dollar.trunc();
+        let result = t + (fractional_dollar - t) * 10.0 / fraction;
+        CalcResult::Number(result)
+    }
+
+    // DOLLARFR(decimal_dollar, fraction)
+    pub(crate) fn fn_dollarfr(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let decimal_dollar = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let mut fraction = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if fraction < 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "fraction should be >= 1".to_string());
+        }
+        if fraction < 1.0 {
+            // this is not necessarily DIV/0
+            return CalcResult::new_error(Error::DIV, cell, "fraction should be >= 1".to_string());
+        }
+        fraction = fraction.trunc();
+        while fraction > 10.0 {
+            fraction /= 10.0;
+        }
+        let t = decimal_dollar.trunc();
+        let result = t + (decimal_dollar - t) * fraction / 10.0;
+        CalcResult::Number(result)
+    }
+
+    // CUMIPMT(rate, nper, pv, start_period, end_period, type)
+    pub(crate) fn fn_cumipmt(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 6 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let rate = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let nper = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let pv = match self.get_number_no_bools(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let start_period = match self.get_number_no_bools(&args[3], cell) {
+            Ok(f) => f.ceil() as i32,
+            Err(s) => return s,
+        };
+        let end_period = match self.get_number_no_bools(&args[4], cell) {
+            Ok(f) => f.trunc() as i32,
+            Err(s) => return s,
+        };
+        // 0 at the end of the period, 1 at the beginning of the period
+        let period_type = match self.get_number_no_bools(&args[5], cell) {
+            Ok(f) => {
+                if f == 0.0 {
+                    false
+                } else if f == 1.0 {
+                    true
+                } else {
+                    return CalcResult::new_error(
+                        Error::NUM,
+                        cell,
+                        "invalid period type".to_string(),
+                    );
+                }
+            }
+            Err(s) => return s,
+        };
+        if start_period > end_period {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "start period should come before end period".to_string(),
+            );
+        }
+        if rate <= 0.0 || nper <= 0.0 || pv <= 0.0 || start_period < 1 {
+            return CalcResult::new_error(Error::NUM, cell, "invalid parameters".to_string());
+        }
+        let mut result = 0.0;
+        for period in start_period..=end_period {
+            result += match compute_ipmt(rate, period as f64, nper, pv, 0.0, period_type) {
+                Ok(f) => f,
+                Err(error) => {
+                    return CalcResult::Error {
+                        error: error.0,
+                        origin: cell,
+                        message: error.1,
+                    }
+                }
+            }
+        }
+        CalcResult::Number(result)
+    }
+
+    // CUMPRINC(rate, nper, pv, start_period, end_period, type)
+    pub(crate) fn fn_cumprinc(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 6 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let rate = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let nper = match self.get_number_no_bools(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let pv = match self.get_number_no_bools(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let start_period = match self.get_number_no_bools(&args[3], cell) {
+            Ok(f) => f.ceil() as i32,
+            Err(s) => return s,
+        };
+        let end_period = match self.get_number_no_bools(&args[4], cell) {
+            Ok(f) => f.trunc() as i32,
+            Err(s) => return s,
+        };
+        // 0 at the end of the period, 1 at the beginning of the period
+        let period_type = match self.get_number_no_bools(&args[5], cell) {
+            Ok(f) => {
+                if f == 0.0 {
+                    false
+                } else if f == 1.0 {
+                    true
+                } else {
+                    return CalcResult::new_error(
+                        Error::NUM,
+                        cell,
+                        "invalid period type".to_string(),
+                    );
+                }
+            }
+            Err(s) => return s,
+        };
+        if start_period > end_period {
+            return CalcResult::new_error(
+                Error::NUM,
+                cell,
+                "start period should come before end period".to_string(),
+            );
+        }
+        if rate <= 0.0 || nper <= 0.0 || pv <= 0.0 || start_period < 1 {
+            return CalcResult::new_error(Error::NUM, cell, "invalid parameters".to_string());
+        }
+        let mut result = 0.0;
+        for period in start_period..=end_period {
+            result += match compute_ppmt(rate, period as f64, nper, pv, 0.0, period_type) {
+                Ok(f) => f,
+                Err(error) => {
+                    return CalcResult::Error {
+                        error: error.0,
+                        origin: cell,
+                        message: error.1,
+                    }
+                }
+            }
+        }
+        CalcResult::Number(result)
+    }
+
+    // DDB(cost, salvage, life, period, [factor])
+    pub(crate) fn fn_ddb(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(4..=5).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let cost = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let salvage = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let life = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let period = match self.get_number(&args[3], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        // The rate at which the balance declines.
+        let factor = if arg_count > 4 {
+            match self.get_number_no_bools(&args[4], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            // If factor is omitted, it is assumed to be 2 (the double-declining balance method).
+            2.0
+        };
+        if period > life || cost < 0.0 || salvage < 0.0 || period <= 0.0 || factor <= 0.0 {
+            return CalcResult::new_error(Error::NUM, cell, "invalid parameters".to_string());
+        };
+        // let period_trunc = period.floor() as i32;
+        let mut rate = factor / life;
+        if rate > 1.0 {
+            rate = 1.0
+        };
+        let value = if rate == 1.0 {
+            if period == 1.0 {
+                cost
+            } else {
+                0.0
+            }
+        } else {
+            cost * (1.0 - rate).powf(period - 1.0)
+        };
+        let new_value = cost * (1.0 - rate).powf(period);
+        let result = f64::max(value - f64::max(salvage, new_value), 0.0);
+        CalcResult::Number(result)
+    }
+
+    // DB(cost, salvage, life, period, [month])
+    pub(crate) fn fn_db(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(4..=5).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let cost = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let salvage = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let life = match self.get_number(&args[2], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let period = match self.get_number(&args[3], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let month = if arg_count > 4 {
+            match self.get_number_no_bools(&args[4], cell) {
+                Ok(f) => f.trunc(),
+                Err(s) => return s,
+            }
+        } else {
+            12.0
+        };
+        if month == 12.0 && period > life
+            || (period > life + 1.0)
+            || month <= 0.0
+            || month > 12.0
+            || period <= 0.0
+            || cost < 0.0
+        {
+            return CalcResult::new_error(Error::NUM, cell, "invalid parameters".to_string());
+        };
+        if cost == 0.0 {
+            return CalcResult::Number(0.0);
+        }
+        // rounded to three decimal places
+        // FIXME: We should have utilities for this (see to_precision)
+        let rate = f64::round((1.0 - f64::powf(salvage / cost, 1.0 / life)) * 1000.0) / 1000.0;
+
+        let mut result = cost * rate * month / 12.0;
+
+        let period = period.floor() as i32;
+        let life = life.floor() as i32;
+
+        // Depreciation for the first and last periods is a special case.
+        if period == 1 {
+            return CalcResult::Number(result);
+        };
+
+        for _ in 0..period - 2 {
+            result += (cost - result) * rate;
+        }
+
+        if period == life + 1 {
+            // last period
+            return CalcResult::Number((cost - result) * rate * (12.0 - month) / 12.0);
+        }
+
+        CalcResult::Number(rate * (cost - result))
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/financial_util.rs.html b/src/ironcalc_base/functions/financial_util.rs.html new file mode 100644 index 0000000..8cc1ee9 --- /dev/null +++ b/src/ironcalc_base/functions/financial_util.rs.html @@ -0,0 +1,511 @@ +financial_util.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+
use crate::expressions::token::Error;
+
+// Here we use some numerical routines to solve for some functions:
+// RATE, IRR, XIRR
+// We use a combination of heuristics, bisection and Newton-Raphson
+// If you want to improve on this methods you probably would want to find failing tests first
+
+// From Microsoft docs:
+// https://support.microsoft.com/en-us/office/rate-function-9f665657-4a7e-4bb7-a030-83fc59e748ce
+// Returns the interest rate per period of an annuity.
+// RATE is calculated by iteration (using Newton-Raphson) and can have zero or more solutions.
+// If the successive results of RATE do not converge to within 0.0000001 after 20 iterations,
+// RATE returns the #NUM! error value.
+// NOTE: We need a better algorithm here
+pub(crate) fn compute_rate(
+    pv: f64,
+    fv: f64,
+    nper: f64,
+    pmt: f64,
+    annuity_type: i32,
+    guess: f64,
+) -> Result<f64, (Error, String)> {
+    let mut rate = guess;
+    // Excel _claims_ to do 20 iterations, but that will have tests failing
+    let max_iterations = 50;
+    let eps = 0.0000001;
+    let annuity_type = annuity_type as f64;
+    if guess <= -1.0 {
+        return Err((Error::VALUE, "Rate initial guess must be > -1".to_string()));
+    }
+    for _ in 1..=max_iterations {
+        let t = (1.0 + rate).powf(nper - 1.0);
+        let tt = t * (1.0 + rate);
+        let f = pv * tt + pmt * (1.0 + rate * annuity_type) * (tt - 1.0) / rate + fv;
+        let f_prime = pv * nper * t - pmt * (tt - 1.0) / (rate * rate)
+            + pmt * (1.0 + rate * annuity_type) * t * nper / rate;
+        let new_rate = rate - f / f_prime;
+        if new_rate <= -1.0 {
+            return Err((Error::NUM, "Failed to converge".to_string()));
+        }
+        if (new_rate - rate).abs() < eps {
+            return Ok(new_rate);
+        }
+        rate = new_rate;
+    }
+    Err((Error::NUM, "Failed to converge".to_string()))
+}
+
+pub(crate) fn compute_npv(rate: f64, values: &[f64]) -> Result<f64, (Error, String)> {
+    let mut npv = 0.0;
+    for (i, item) in values.iter().enumerate() {
+        npv += item / (1.0 + rate).powi(i as i32 + 1)
+    }
+    Ok(npv)
+}
+
+// Tries to solve npv(r, values) = 0 for r given values
+// Uses a bit of heuristics:
+// * First tries Newton-Raphson around the guess
+// * Failing that uses bisection and bracketing
+// * If that fails (no root found of the interval) uses Newton-Raphson around the edges
+// Values for x1, x2 and the guess for N-R are fine tuned using heuristics
+pub(crate) fn compute_irr(values: &[f64], guess: f64) -> Result<f64, (Error, String)> {
+    if guess <= -1.0 {
+        return Err((Error::VALUE, "Rate initial guess must be > -1".to_string()));
+    }
+    // The values cannot be all positive or all negative
+    if values.iter().all(|&x| x >= 0.0) || values.iter().all(|&x| x <= 0.0) {
+        return Err((Error::NUM, "Failed to converge".to_string()));
+    }
+    if let Ok(f) = compute_irr_newton_raphson(values, guess) {
+        return Ok(f);
+    };
+    // We try bisection
+    let max_iterations = 50;
+    let eps = 1e-10;
+    let x1 = -0.99999;
+    let x2 = 100.0;
+    let f1 = compute_npv(x1, values)?;
+    let f2 = compute_npv(x2, values)?;
+    if f1 * f2 > 0.0 {
+        // The root is not within the limits or there are two roots
+        // We try Newton-Raphson a bit above the upper limit and a bit below the lower limit
+        if let Ok(f) = compute_irr_newton_raphson(values, 200.0) {
+            return Ok(f);
+        };
+        if let Ok(f) = compute_irr_newton_raphson(values, -2.0) {
+            return Ok(f);
+        };
+        return Err((Error::NUM, "Failed to converge".to_string()));
+    }
+    let (mut rtb, mut dx) = if f1 < 0.0 {
+        (x1, x2 - x1)
+    } else {
+        (x2, x1 - x2)
+    };
+    for _ in 1..max_iterations {
+        dx *= 0.5;
+        let x_mid = rtb + dx;
+        let f_mid = compute_npv(x_mid, values)?;
+        if f_mid <= 0.0 {
+            rtb = x_mid;
+        }
+        if f_mid.abs() < eps || dx.abs() < eps {
+            return Ok(x_mid);
+        }
+    }
+
+    Err((Error::NUM, "Failed to converge".to_string()))
+}
+
+fn compute_npv_prime(rate: f64, values: &[f64]) -> Result<f64, (Error, String)> {
+    let mut npv = 0.0;
+    for (i, item) in values.iter().enumerate() {
+        npv += -item * (i as f64 + 1.0) / (1.0 + rate).powi(i as i32 + 2)
+    }
+    if npv.is_infinite() || npv.is_nan() {
+        return Err((Error::NUM, "NaN".to_string()));
+    }
+    Ok(npv)
+}
+
+fn compute_irr_newton_raphson(values: &[f64], guess: f64) -> Result<f64, (Error, String)> {
+    let mut irr = guess;
+    let max_iterations = 50;
+    let eps = 1e-8;
+    for _ in 1..=max_iterations {
+        let f = compute_npv(irr, values)?;
+        let f_prime = compute_npv_prime(irr, values)?;
+        let new_irr = irr - f / f_prime;
+        if (new_irr - irr).abs() < eps {
+            return Ok(new_irr);
+        }
+        irr = new_irr;
+    }
+    Err((Error::NUM, "Failed to converge".to_string()))
+}
+
+// Formula is:
+//  $$\sum_{i=1}^n\frac{v_i}{(1+r)^{\frac{(d_j-d_1)}{365}}}$$
+// where $v_i$ is the ith-1 value and $d_i$ is the ith-1 date
+pub(crate) fn compute_xnpv(
+    rate: f64,
+    values: &[f64],
+    dates: &[f64],
+) -> Result<f64, (Error, String)> {
+    let mut xnpv = values[0];
+    let d0 = dates[0];
+    let n = values.len();
+    for i in 1..n {
+        let vi = values[i];
+        let di = dates[i];
+        xnpv += vi / ((1.0 + rate).powf((di - d0) / 365.0))
+    }
+    if xnpv.is_infinite() || xnpv.is_nan() {
+        return Err((Error::NUM, "NaN".to_string()));
+    }
+    Ok(xnpv)
+}
+
+fn compute_xnpv_prime(rate: f64, values: &[f64], dates: &[f64]) -> Result<f64, (Error, String)> {
+    let mut xnpv = 0.0;
+    let d0 = dates[0];
+    let n = values.len();
+    for i in 1..n {
+        let vi = values[i];
+        let di = dates[i];
+        let ratio = (di - d0) / 365.0;
+        let power = (1.0 + rate).powf(ratio + 1.0);
+        xnpv -= vi * ratio / power;
+    }
+    if xnpv.is_infinite() || xnpv.is_nan() {
+        return Err((Error::NUM, "NaN".to_string()));
+    }
+    Ok(xnpv)
+}
+
+fn compute_xirr_newton_raphson(
+    values: &[f64],
+    dates: &[f64],
+    guess: f64,
+) -> Result<f64, (Error, String)> {
+    let mut xirr = guess;
+    let max_iterations = 100;
+    let eps = 1e-7;
+    for _ in 1..=max_iterations {
+        let f = compute_xnpv(xirr, values, dates)?;
+        let f_prime = compute_xnpv_prime(xirr, values, dates)?;
+        let new_xirr = xirr - f / f_prime;
+        if (new_xirr - xirr).abs() < eps {
+            return Ok(new_xirr);
+        }
+        xirr = new_xirr;
+    }
+    Err((Error::NUM, "Failed to converge".to_string()))
+}
+
+// NOTES:
+// 1. If the cash-flows (value[i] for i > 0) are always of the same sign, the function is monotonous
+// 3. Say (d_max, v_max) are the pair where d_max is the largest date,
+//    then if v_max and v_0 have different signs, it's guaranteed there is a zero
+// The algorithm needs to be improved but it works so far in all practical cases
+pub(crate) fn compute_xirr(
+    values: &[f64],
+    dates: &[f64],
+    guess: f64,
+) -> Result<f64, (Error, String)> {
+    if guess <= -1.0 {
+        return Err((Error::VALUE, "Rate initial guess must be > -1".to_string()));
+    }
+    // The values cannot be all positive or all negative
+    if values.iter().all(|&x| x >= 0.0) || values.iter().all(|&x| x <= 0.0) {
+        return Err((Error::NUM, "Failed to converge".to_string()));
+    }
+    if let Ok(f) = compute_xirr_newton_raphson(values, dates, guess) {
+        return Ok(f);
+    };
+    // We try bisection
+    let max_iterations = 50;
+    let eps = 1e-8;
+    // This will miss 0's very close to -1
+    let x1 = -0.9999;
+    let x2 = 100.0;
+    let f1 = compute_xnpv(x1, values, dates)?;
+    let f2 = compute_xnpv(x2, values, dates)?;
+    if f1 * f2 > 0.0 {
+        // The root is not within the limits (or there are two roots)
+        // We try Newton-Raphson above the upper limit
+        // (we cannot go to the left of -1)
+        if let Ok(f) = compute_xirr_newton_raphson(values, dates, 200.0) {
+            return Ok(f);
+        };
+        return Err((Error::NUM, "Failed to converge".to_string()));
+    }
+
+    let (mut rtb, mut dx) = if f1 < 0.0 {
+        (x1, x2 - x1)
+    } else {
+        (x2, x1 - x2)
+    };
+
+    for _ in 1..max_iterations {
+        dx *= 0.5;
+        let x_mid = rtb + dx;
+        let f_mid = compute_xnpv(x_mid, values, dates)?;
+        if f_mid <= 0.0 {
+            rtb = x_mid;
+        }
+        if f_mid.abs() < eps || dx.abs() < eps {
+            return Ok(x_mid);
+        }
+    }
+
+    Err((Error::NUM, "Failed to converge".to_string()))
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/information.rs.html b/src/ironcalc_base/functions/information.rs.html new file mode 100644 index 0000000..28a4da1 --- /dev/null +++ b/src/ironcalc_base/functions/information.rs.html @@ -0,0 +1,593 @@ +information.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+
use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::parser::Node,
+    expressions::token::Error,
+    model::{Model, ParsedDefinedName},
+};
+
+impl Model {
+    pub(crate) fn fn_isnumber(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::Number(_) => return CalcResult::Boolean(true),
+                _ => {
+                    return CalcResult::Boolean(false);
+                }
+            };
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+    pub(crate) fn fn_istext(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::String(_) => return CalcResult::Boolean(true),
+                _ => {
+                    return CalcResult::Boolean(false);
+                }
+            };
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+    pub(crate) fn fn_isnontext(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::String(_) => return CalcResult::Boolean(false),
+                _ => {
+                    return CalcResult::Boolean(true);
+                }
+            };
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+    pub(crate) fn fn_islogical(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::Boolean(_) => return CalcResult::Boolean(true),
+                _ => {
+                    return CalcResult::Boolean(false);
+                }
+            };
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+    pub(crate) fn fn_isblank(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::EmptyCell => return CalcResult::Boolean(true),
+                _ => {
+                    return CalcResult::Boolean(false);
+                }
+            };
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+    pub(crate) fn fn_iserror(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::Error { .. } => return CalcResult::Boolean(true),
+                _ => {
+                    return CalcResult::Boolean(false);
+                }
+            };
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+    pub(crate) fn fn_iserr(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::Error { error, .. } => {
+                    if Error::NA == error {
+                        return CalcResult::Boolean(false);
+                    } else {
+                        return CalcResult::Boolean(true);
+                    }
+                }
+                _ => {
+                    return CalcResult::Boolean(false);
+                }
+            };
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+    pub(crate) fn fn_isna(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::Error { error, .. } => {
+                    if error == Error::NA {
+                        return CalcResult::Boolean(true);
+                    } else {
+                        return CalcResult::Boolean(false);
+                    }
+                }
+                _ => {
+                    return CalcResult::Boolean(false);
+                }
+            };
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+
+    // Returns true if it is a reference or evaluates to a reference
+    // But DOES NOT evaluate
+    pub(crate) fn fn_isref(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        match &args[0] {
+            Node::ReferenceKind { .. } | Node::RangeKind { .. } | Node::OpRangeKind { .. } => {
+                CalcResult::Boolean(true)
+            }
+            Node::FunctionKind { kind, args: _ } => CalcResult::Boolean(kind.returns_reference()),
+            _ => CalcResult::Boolean(false),
+        }
+    }
+
+    pub(crate) fn fn_isodd(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f.abs().trunc() as i64,
+            Err(s) => return s,
+        };
+        CalcResult::Boolean(value % 2 == 1)
+    }
+
+    pub(crate) fn fn_iseven(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number_no_bools(&args[0], cell) {
+            Ok(f) => f.abs().trunc() as i64,
+            Err(s) => return s,
+        };
+        CalcResult::Boolean(value % 2 == 0)
+    }
+
+    // ISFORMULA arg needs to be a reference or something that evaluates to a reference
+    pub(crate) fn fn_isformula(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        if let CalcResult::Range { left, right } = self.evaluate_node_with_reference(&args[0], cell)
+        {
+            if left.sheet != right.sheet {
+                return CalcResult::Error {
+                    error: Error::ERROR,
+                    origin: cell,
+                    message: "3D ranges not supported".to_string(),
+                };
+            }
+            if left.row != right.row && left.column != right.column {
+                // FIXME: Implicit intersection or dynamic arrays
+                return CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "argument must be a reference to a single cell".to_string(),
+                };
+            }
+            let is_formula = if let Ok(f) = self.cell_formula(left.sheet, left.row, left.column) {
+                f.is_some()
+            } else {
+                false
+            };
+            CalcResult::Boolean(is_formula)
+        } else {
+            CalcResult::Error {
+                error: Error::ERROR,
+                origin: cell,
+                message: "Argument must be a reference".to_string(),
+            }
+        }
+    }
+
+    pub(crate) fn fn_errortype(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        match self.evaluate_node_in_context(&args[0], cell) {
+            CalcResult::Error { error, .. } => {
+                match error {
+                    Error::NULL => CalcResult::Number(1.0),
+                    Error::DIV => CalcResult::Number(2.0),
+                    Error::VALUE => CalcResult::Number(3.0),
+                    Error::REF => CalcResult::Number(4.0),
+                    Error::NAME => CalcResult::Number(5.0),
+                    Error::NUM => CalcResult::Number(6.0),
+                    Error::NA => CalcResult::Number(7.0),
+                    Error::SPILL => CalcResult::Number(9.0),
+                    Error::CALC => CalcResult::Number(14.0),
+                    // IronCalc specific
+                    Error::ERROR => CalcResult::Number(101.0),
+                    Error::NIMPL => CalcResult::Number(102.0),
+                    Error::CIRC => CalcResult::Number(104.0),
+                    // Missing from Excel
+                    // #GETTING_DATA => 8
+                    // #CONNECT => 10
+                    // #BLOCKED => 11
+                    // #UNKNOWN => 12
+                    // #FIELD => 13
+                    // #EXTERNAL => 19
+                }
+            }
+            _ => CalcResult::Error {
+                error: Error::NA,
+                origin: cell,
+                message: "Not an error".to_string(),
+            },
+        }
+    }
+
+    // Excel believes for some reason that TYPE(A1:A7) is an array formula
+    // Although we evaluate the same as Excel we cannot, ATM import this from excel
+    pub(crate) fn fn_type(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        match self.evaluate_node_in_context(&args[0], cell) {
+            CalcResult::String(_) => CalcResult::Number(2.0),
+            CalcResult::Number(_) => CalcResult::Number(1.0),
+            CalcResult::Boolean(_) => CalcResult::Number(4.0),
+            CalcResult::Error { .. } => CalcResult::Number(16.0),
+            CalcResult::Range { .. } => CalcResult::Number(64.0),
+            CalcResult::EmptyCell => CalcResult::Number(1.0),
+            CalcResult::EmptyArg => {
+                // This cannot happen
+                CalcResult::Number(1.0)
+            }
+        }
+    }
+    pub(crate) fn fn_sheet(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if arg_count > 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        if arg_count == 0 {
+            // Sheets are 0-indexed`
+            return CalcResult::Number(cell.sheet as f64 + 1.0);
+        }
+        // The arg could be a defined name or a table
+        let arg = &args[0];
+        if let Node::VariableKind(name) = arg {
+            // Let's see if it is a defined name
+            if let Some(defined_name) = self.parsed_defined_names.get(&(None, name.to_lowercase()))
+            {
+                match defined_name {
+                    ParsedDefinedName::CellReference(reference) => {
+                        return CalcResult::Number(reference.sheet as f64 + 1.0)
+                    }
+                    ParsedDefinedName::RangeReference(range) => {
+                        return CalcResult::Number(range.left.sheet as f64 + 1.0)
+                    }
+                    ParsedDefinedName::InvalidDefinedNameFormula => {
+                        return CalcResult::Error {
+                            error: Error::NA,
+                            origin: cell,
+                            message: "Invalid name".to_string(),
+                        };
+                    }
+                }
+            }
+            // Now let's see if it is a table
+            for (table_name, table) in &self.workbook.tables {
+                if table_name == name {
+                    if let Some(sheet_index) = self.get_sheet_index_by_name(&table.sheet_name) {
+                        return CalcResult::Number(sheet_index as f64 + 1.0);
+                    } else {
+                        break;
+                    }
+                }
+            }
+        }
+        // Now it should be the name of a sheet
+        let sheet_name = match self.get_string(arg, cell) {
+            Ok(s) => s,
+            Err(e) => return e,
+        };
+        if let Some(sheet_index) = self.get_sheet_index_by_name(&sheet_name) {
+            return CalcResult::Number(sheet_index as f64 + 1.0);
+        }
+        CalcResult::Error {
+            error: Error::NA,
+            origin: cell,
+            message: "Invalid name".to_string(),
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/logical.rs.html b/src/ironcalc_base/functions/logical.rs.html new file mode 100644 index 0000000..2995d77 --- /dev/null +++ b/src/ironcalc_base/functions/logical.rs.html @@ -0,0 +1,643 @@ +logical.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+
use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::parser::Node,
+    expressions::token::Error,
+    model::Model,
+};
+
+use super::util::compare_values;
+
+impl Model {
+    pub(crate) fn fn_if(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 2 || args.len() == 3 {
+            let cond_result = self.get_boolean(&args[0], cell);
+            let cond = match cond_result {
+                Ok(f) => f,
+                Err(s) => {
+                    return s;
+                }
+            };
+            if cond {
+                return self.evaluate_node_in_context(&args[1], cell);
+            } else if args.len() == 3 {
+                return self.evaluate_node_in_context(&args[2], cell);
+            } else {
+                return CalcResult::Boolean(false);
+            }
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+
+    pub(crate) fn fn_iferror(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 2 {
+            let value = self.evaluate_node_in_context(&args[0], cell);
+            match value {
+                CalcResult::Error { .. } => {
+                    return self.evaluate_node_in_context(&args[1], cell);
+                }
+                _ => return value,
+            }
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+
+    pub(crate) fn fn_ifna(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 2 {
+            let value = self.evaluate_node_in_context(&args[0], cell);
+            if let CalcResult::Error { error, .. } = &value {
+                if error == &Error::NA {
+                    return self.evaluate_node_in_context(&args[1], cell);
+                }
+            }
+            return value;
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+
+    pub(crate) fn fn_not(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            match self.get_boolean(&args[0], cell) {
+                Ok(f) => return CalcResult::Boolean(!f),
+                Err(s) => {
+                    return s;
+                }
+            };
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+
+    pub(crate) fn fn_and(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let mut true_count = 0;
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::Boolean(b) => {
+                    if !b {
+                        return CalcResult::Boolean(false);
+                    }
+                    true_count += 1;
+                }
+                CalcResult::Number(value) => {
+                    if value == 0.0 {
+                        return CalcResult::Boolean(false);
+                    }
+                    true_count += 1;
+                }
+                CalcResult::String(_value) => {
+                    true_count += 1;
+                }
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    for row in left.row..(right.row + 1) {
+                        for column in left.column..(right.column + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::Boolean(b) => {
+                                    if !b {
+                                        return CalcResult::Boolean(false);
+                                    }
+                                    true_count += 1;
+                                }
+                                CalcResult::Number(value) => {
+                                    if value == 0.0 {
+                                        return CalcResult::Boolean(false);
+                                    }
+                                    true_count += 1;
+                                }
+                                CalcResult::String(_value) => {
+                                    true_count += 1;
+                                }
+                                error @ CalcResult::Error { .. } => return error,
+                                CalcResult::Range { .. } => {}
+                                CalcResult::EmptyCell | CalcResult::EmptyArg => {}
+                            }
+                        }
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::EmptyCell | CalcResult::EmptyArg => {}
+            };
+        }
+        if true_count == 0 {
+            return CalcResult::new_error(
+                Error::VALUE,
+                cell,
+                "Boolean values not found".to_string(),
+            );
+        }
+        CalcResult::Boolean(true)
+    }
+
+    pub(crate) fn fn_or(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let mut result = false;
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::Boolean(value) => result = value || result,
+                CalcResult::Number(value) => {
+                    if value != 0.0 {
+                        return CalcResult::Boolean(true);
+                    }
+                }
+                CalcResult::String(_value) => {
+                    return CalcResult::Boolean(true);
+                }
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    for row in left.row..(right.row + 1) {
+                        for column in left.column..(right.column + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::Boolean(value) => {
+                                    result = value || result;
+                                }
+                                CalcResult::Number(value) => {
+                                    if value != 0.0 {
+                                        return CalcResult::Boolean(true);
+                                    }
+                                }
+                                CalcResult::String(_value) => {
+                                    return CalcResult::Boolean(true);
+                                }
+                                error @ CalcResult::Error { .. } => return error,
+                                CalcResult::Range { .. } => {}
+                                CalcResult::EmptyCell | CalcResult::EmptyArg => {}
+                            }
+                        }
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::EmptyCell | CalcResult::EmptyArg => {}
+            };
+        }
+        CalcResult::Boolean(result)
+    }
+
+    /// XOR(logical1, [logical]*,...)
+    /// Logical1 is required, subsequent logical values are optional. Can be logical values, arrays, or references.
+    /// The result of XOR is TRUE when the number of TRUE inputs is odd and FALSE when the number of TRUE inputs is even.
+    pub(crate) fn fn_xor(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let mut true_count = 0;
+        let mut false_count = 0;
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::Boolean(b) => {
+                    if b {
+                        true_count += 1;
+                    } else {
+                        false_count += 1;
+                    }
+                }
+                CalcResult::Number(value) => {
+                    if value != 0.0 {
+                        true_count += 1;
+                    } else {
+                        false_count += 1;
+                    }
+                }
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    for row in left.row..(right.row + 1) {
+                        for column in left.column..(right.column + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::Boolean(b) => {
+                                    if b {
+                                        true_count += 1;
+                                    } else {
+                                        false_count += 1;
+                                    }
+                                }
+                                CalcResult::Number(value) => {
+                                    if value != 0.0 {
+                                        true_count += 1;
+                                    } else {
+                                        false_count += 1;
+                                    }
+                                }
+                                _ => {}
+                            }
+                        }
+                    }
+                }
+                _ => {}
+            };
+        }
+        if true_count == 0 && false_count == 0 {
+            return CalcResult::new_error(Error::VALUE, cell, "No booleans found".to_string());
+        }
+        CalcResult::Boolean(true_count % 2 == 1)
+    }
+
+    /// =SWITCH(expression, case1, value1, [case, value]*, [default])
+    pub(crate) fn fn_switch(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let args_count = args.len();
+        if args_count < 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        // TODO add implicit intersection
+        let expr = self.evaluate_node_in_context(&args[0], cell);
+        if expr.is_error() {
+            return expr;
+        }
+
+        // How many cases we have?
+        // 3, 4 args -> 1 case
+        let case_count = (args_count - 1) / 2;
+        for case_index in 0..case_count {
+            let case = self.evaluate_node_in_context(&args[2 * case_index + 1], cell);
+            if case.is_error() {
+                return case;
+            }
+            if compare_values(&expr, &case) == 0 {
+                return self.evaluate_node_in_context(&args[2 * case_index + 2], cell);
+            }
+        }
+        // None of the cases matched so we return the default
+        // If there is an even number of args is the last one otherwise is #N/A
+        if args_count % 2 == 0 {
+            return self.evaluate_node_in_context(&args[args_count - 1], cell);
+        }
+        CalcResult::Error {
+            error: Error::NA,
+            origin: cell,
+            message: "Did not find a match".to_string(),
+        }
+    }
+
+    /// =IFS(condition1, value, [condition, value]*)
+    pub(crate) fn fn_ifs(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let args_count = args.len();
+        if args_count < 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        if args_count % 2 != 0 {
+            // Missing value for last condition
+            return CalcResult::new_args_number_error(cell);
+        }
+        let case_count = args_count / 2;
+        for case_index in 0..case_count {
+            let value = self.get_boolean(&args[2 * case_index], cell);
+            match value {
+                Ok(b) => {
+                    if b {
+                        return self.evaluate_node_in_context(&args[2 * case_index + 1], cell);
+                    }
+                }
+                Err(s) => return s,
+            }
+        }
+        CalcResult::Error {
+            error: Error::NA,
+            origin: cell,
+            message: "Did not find a match".to_string(),
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/lookup_and_reference.rs.html b/src/ironcalc_base/functions/lookup_and_reference.rs.html new file mode 100644 index 0000000..7e6967a --- /dev/null +++ b/src/ironcalc_base/functions/lookup_and_reference.rs.html @@ -0,0 +1,1687 @@ +lookup_and_reference.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+
use crate::constants::{LAST_COLUMN, LAST_ROW};
+use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::parser::Node,
+    expressions::token::Error,
+    model::Model,
+    utils::ParsedReference,
+};
+
+use super::util::{compare_values, from_wildcard_to_regex, result_matches_regex, values_are_equal};
+
+impl Model {
+    pub(crate) fn fn_index(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let row_num;
+        let col_num;
+        if args.len() == 3 {
+            row_num = match self.get_number(&args[1], cell) {
+                Ok(f) => f,
+                Err(s) => {
+                    return s;
+                }
+            };
+            if row_num < 1.0 {
+                return CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "Argument must be >= 1".to_string(),
+                };
+            }
+            col_num = match self.get_number(&args[2], cell) {
+                Ok(f) => f,
+                Err(s) => {
+                    return s;
+                }
+            };
+            if col_num < 1.0 {
+                return CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "Argument must be >= 1".to_string(),
+                };
+            }
+        } else if args.len() == 2 {
+            row_num = match self.get_number(&args[1], cell) {
+                Ok(f) => f,
+                Err(s) => {
+                    return s;
+                }
+            };
+            if row_num < 1.0 {
+                return CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "Argument must be >= 1".to_string(),
+                };
+            }
+            col_num = -1.0;
+        } else {
+            return CalcResult::new_args_number_error(cell);
+        }
+        match self.evaluate_node_in_context(&args[0], cell) {
+            CalcResult::Range { left, right } => {
+                let row;
+                let column;
+                if (col_num + 1.0).abs() < f64::EPSILON {
+                    if left.row == right.row {
+                        column = left.column + (row_num as i32) - 1;
+                        row = left.row;
+                    } else {
+                        column = left.column;
+                        row = left.row + (row_num as i32) - 1;
+                    }
+                } else {
+                    row = left.row + (row_num as i32) - 1;
+                    column = left.column + (col_num as i32) - 1;
+                }
+                if row > right.row {
+                    return CalcResult::Error {
+                        error: Error::REF,
+                        origin: cell,
+                        message: "Wrong reference".to_string(),
+                    };
+                }
+                if column > right.column {
+                    return CalcResult::Error {
+                        error: Error::REF,
+                        origin: cell,
+                        message: "Wrong reference".to_string(),
+                    };
+                }
+                self.evaluate_cell(CellReference {
+                    sheet: left.sheet,
+                    row,
+                    column,
+                })
+            }
+            error @ CalcResult::Error { .. } => error,
+            _ => CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "Expecting a Range".to_string(),
+            },
+        }
+    }
+
+    //     MATCH(lookup_value, lookup_array, [match_type])
+    // The MATCH function syntax has the following arguments:
+    //   * lookup_value    Required. The value that you want to match in lookup_array.
+    //                     The lookup_value argument can be a value (number, text, or logical value)
+    //                     or a cell reference to a number, text, or logical value.
+    //   * lookup_array    Required. The range of cells being searched.
+    //   * match_type      Optional. The number -1, 0, or 1.
+    //                     The match_type argument specifies how Excel matches lookup_value
+    //                     with values in lookup_array. The default value for this argument is 1.
+    // NOTE: Please read the caveat above in binary search
+    pub(crate) fn fn_match(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() > 3 || args.len() < 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let target = self.evaluate_node_in_context(&args[0], cell);
+        if target.is_error() {
+            return target;
+        }
+        if matches!(target, CalcResult::EmptyCell) {
+            return CalcResult::Error {
+                error: Error::NA,
+                origin: cell,
+                message: "Cannot match empty cell".to_string(),
+            };
+        }
+        let match_type = if args.len() == 3 {
+            match self.get_number(&args[2], cell) {
+                Ok(v) => v as i32,
+                Err(s) => return s,
+            }
+        } else {
+            1
+        };
+        let match_range = self.evaluate_node_in_context(&args[1], cell);
+
+        match match_range {
+            CalcResult::Range { left, right } => {
+                match match_type {
+                    -1 => {
+                        // We apply binary search leftmost for value in the range
+                        let is_row_vector;
+                        if left.row == right.row {
+                            is_row_vector = false;
+                        } else if left.column == right.column {
+                            is_row_vector = true;
+                        } else {
+                            // second argument must be a vector
+                            return CalcResult::Error {
+                                error: Error::ERROR,
+                                origin: cell,
+                                message: "Argument must be a vector".to_string(),
+                            };
+                        }
+                        let n = if is_row_vector {
+                            right.row - left.row
+                        } else {
+                            right.column - left.column
+                        } + 1;
+                        let mut l = 0;
+                        let mut r = n;
+                        while l < r {
+                            let m = (l + r) / 2;
+                            let row;
+                            let column;
+                            if is_row_vector {
+                                row = left.row + m;
+                                column = left.column;
+                            } else {
+                                column = left.column + m;
+                                row = left.row;
+                            }
+                            let value = self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            });
+
+                            if compare_values(&value, &target) >= 0 {
+                                l = m + 1;
+                            } else {
+                                r = m;
+                            }
+                        }
+                        // r is the number of elements less than target in the vector
+                        // If target is less than the minimum return #N/A
+                        if l == 0 {
+                            return CalcResult::Error {
+                                error: Error::NA,
+                                origin: cell,
+                                message: "Not found".to_string(),
+                            };
+                        }
+                        // Now l points to the leftmost element
+                        CalcResult::Number(l as f64)
+                    }
+                    0 => {
+                        // We apply linear search
+                        let is_row_vector;
+                        if left.row == right.row {
+                            is_row_vector = false;
+                        } else if left.column == right.column {
+                            is_row_vector = true;
+                        } else {
+                            // second argument must be a vector
+                            return CalcResult::Error {
+                                error: Error::ERROR,
+                                origin: cell,
+                                message: "Argument must be a vector".to_string(),
+                            };
+                        }
+                        let n = if is_row_vector {
+                            right.row - left.row
+                        } else {
+                            right.column - left.column
+                        } + 1;
+                        let result_matches: Box<dyn Fn(&CalcResult) -> bool> =
+                            if let CalcResult::String(s) = &target {
+                                if let Ok(reg) = from_wildcard_to_regex(&s.to_lowercase(), true) {
+                                    Box::new(move |x| result_matches_regex(x, &reg))
+                                } else {
+                                    Box::new(move |_| false)
+                                }
+                            } else {
+                                Box::new(move |x| values_are_equal(x, &target))
+                            };
+                        for l in 0..n {
+                            let row;
+                            let column;
+                            if is_row_vector {
+                                row = left.row + l;
+                                column = left.column;
+                            } else {
+                                column = left.column + l;
+                                row = left.row;
+                            }
+                            let value = self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            });
+                            if result_matches(&value) {
+                                return CalcResult::Number(l as f64 + 1.0);
+                            }
+                        }
+                        CalcResult::Error {
+                            error: Error::NA,
+                            origin: cell,
+                            message: "Not found".to_string(),
+                        }
+                    }
+                    _ => {
+                        // l is the number of elements less than target in the vector
+                        let is_row_vector;
+                        if left.row == right.row {
+                            is_row_vector = false;
+                        } else if left.column == right.column {
+                            is_row_vector = true;
+                        } else {
+                            // second argument must be a vector
+                            return CalcResult::Error {
+                                error: Error::ERROR,
+                                origin: cell,
+                                message: "Argument must be a vector".to_string(),
+                            };
+                        }
+                        let l = self.binary_search(&target, &left, &right, is_row_vector);
+                        if l == -2 {
+                            return CalcResult::Error {
+                                error: Error::NA,
+                                origin: cell,
+                                message: "Not found".to_string(),
+                            };
+                        }
+
+                        CalcResult::Number(l as f64 + 1.0)
+                    }
+                }
+            }
+            error @ CalcResult::Error { .. } => error,
+            _ => CalcResult::Error {
+                error: Error::NA,
+                origin: cell,
+                message: "Invalid".to_string(),
+            },
+        }
+    }
+
+    /// HLOOKUP(lookup_value, table_array, row_index, [is_sorted])
+    /// We look for `lookup_value` in the first row of table array
+    /// We return the value in row `row_index` of the same column in `table_array`
+    /// `is_sorted` is true by default and assumes that values in first row are ordered
+    pub(crate) fn fn_hlookup(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() > 4 || args.len() < 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let lookup_value = self.evaluate_node_in_context(&args[0], cell);
+        if lookup_value.is_error() {
+            return lookup_value;
+        }
+        let row_index = match self.get_number(&args[2], cell) {
+            Ok(v) => v.floor() as i32,
+            Err(s) => return s,
+        };
+        let is_sorted = if args.len() == 4 {
+            match self.get_boolean(&args[3], cell) {
+                Ok(v) => v,
+                Err(s) => return s,
+            }
+        } else {
+            true
+        };
+        let range = self.evaluate_node_in_context(&args[1], cell);
+        match range {
+            CalcResult::Range { left, right } => {
+                if is_sorted {
+                    // This assumes the values in row are in order
+                    let l = self.binary_search(&lookup_value, &left, &right, false);
+                    if l == -2 {
+                        return CalcResult::Error {
+                            error: Error::NA,
+                            origin: cell,
+                            message: "Not found".to_string(),
+                        };
+                    }
+                    let row = left.row + row_index - 1;
+                    let column = left.column + l;
+                    if row > right.row {
+                        return CalcResult::Error {
+                            error: Error::REF,
+                            origin: cell,
+                            message: "Invalid reference".to_string(),
+                        };
+                    }
+                    self.evaluate_cell(CellReference {
+                        sheet: left.sheet,
+                        row,
+                        column,
+                    })
+                } else {
+                    // Linear search for exact match
+                    let n = right.column - left.column + 1;
+                    let row = left.row + row_index - 1;
+                    if row > right.row {
+                        return CalcResult::Error {
+                            error: Error::REF,
+                            origin: cell,
+                            message: "Invalid reference".to_string(),
+                        };
+                    }
+                    let result_matches: Box<dyn Fn(&CalcResult) -> bool> =
+                        if let CalcResult::String(s) = &lookup_value {
+                            if let Ok(reg) = from_wildcard_to_regex(&s.to_lowercase(), true) {
+                                Box::new(move |x| result_matches_regex(x, &reg))
+                            } else {
+                                Box::new(move |_| false)
+                            }
+                        } else {
+                            Box::new(move |x| compare_values(x, &lookup_value) == 0)
+                        };
+                    for l in 0..n {
+                        let value = self.evaluate_cell(CellReference {
+                            sheet: left.sheet,
+                            row: left.row,
+                            column: left.column + l,
+                        });
+                        if result_matches(&value) {
+                            return self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column: left.column + l,
+                            });
+                        }
+                    }
+                    CalcResult::Error {
+                        error: Error::NA,
+                        origin: cell,
+                        message: "Not found".to_string(),
+                    }
+                }
+            }
+            error @ CalcResult::Error { .. } => error,
+            CalcResult::String(_) => CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "Range expected".to_string(),
+            },
+            _ => CalcResult::Error {
+                error: Error::NA,
+                origin: cell,
+                message: "Range expected".to_string(),
+            },
+        }
+    }
+
+    /// VLOOKUP(lookup_value, table_array, row_index, [is_sorted])
+    /// We look for `lookup_value` in the first column of table array
+    /// We return the value in column `column_index` of the same row in `table_array`
+    /// `is_sorted` is true by default and assumes that values in first column are ordered
+    pub(crate) fn fn_vlookup(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() > 4 || args.len() < 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let lookup_value = self.evaluate_node_in_context(&args[0], cell);
+        if lookup_value.is_error() {
+            return lookup_value;
+        }
+        let column_index = match self.get_number(&args[2], cell) {
+            Ok(v) => v.floor() as i32,
+            Err(s) => return s,
+        };
+        let is_sorted = if args.len() == 4 {
+            match self.get_boolean(&args[3], cell) {
+                Ok(v) => v,
+                Err(s) => return s,
+            }
+        } else {
+            true
+        };
+        let range = self.evaluate_node_in_context(&args[1], cell);
+        match range {
+            CalcResult::Range { left, right } => {
+                if is_sorted {
+                    // This assumes the values in column are in order
+                    let l = self.binary_search(&lookup_value, &left, &right, true);
+                    if l == -2 {
+                        return CalcResult::Error {
+                            error: Error::NA,
+                            origin: cell,
+                            message: "Not found".to_string(),
+                        };
+                    }
+                    let row = left.row + l;
+                    let column = left.column + column_index - 1;
+                    if column > right.column {
+                        return CalcResult::Error {
+                            error: Error::REF,
+                            origin: cell,
+                            message: "Invalid reference".to_string(),
+                        };
+                    }
+                    self.evaluate_cell(CellReference {
+                        sheet: left.sheet,
+                        row,
+                        column,
+                    })
+                } else {
+                    // Linear search for exact match
+                    let n = right.row - left.row + 1;
+                    let column = left.column + column_index - 1;
+                    if column > right.column {
+                        return CalcResult::Error {
+                            error: Error::REF,
+                            origin: cell,
+                            message: "Invalid reference".to_string(),
+                        };
+                    }
+                    let result_matches: Box<dyn Fn(&CalcResult) -> bool> =
+                        if let CalcResult::String(s) = &lookup_value {
+                            if let Ok(reg) = from_wildcard_to_regex(&s.to_lowercase(), true) {
+                                Box::new(move |x| result_matches_regex(x, &reg))
+                            } else {
+                                Box::new(move |_| false)
+                            }
+                        } else {
+                            Box::new(move |x| compare_values(x, &lookup_value) == 0)
+                        };
+                    for l in 0..n {
+                        let value = self.evaluate_cell(CellReference {
+                            sheet: left.sheet,
+                            row: left.row + l,
+                            column: left.column,
+                        });
+                        if result_matches(&value) {
+                            return self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row: left.row + l,
+                                column,
+                            });
+                        }
+                    }
+                    CalcResult::Error {
+                        error: Error::NA,
+                        origin: cell,
+                        message: "Not found".to_string(),
+                    }
+                }
+            }
+            error @ CalcResult::Error { .. } => error,
+            CalcResult::String(_) => CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "Range expected".to_string(),
+            },
+            _ => CalcResult::Error {
+                error: Error::NA,
+                origin: cell,
+                message: "Range expected".to_string(),
+            },
+        }
+    }
+
+    // LOOKUP(lookup_value, lookup_vector, [result_vector])
+    // Important: The values in lookup_vector must be placed in ascending order:
+    // ..., -2, -1, 0, 1, 2, ..., A-Z, FALSE, TRUE;
+    // otherwise, LOOKUP might not return the correct value.
+    // Uppercase and lowercase text are equivalent.
+    // TODO: Implement the other form of INDEX:
+    // INDEX(reference, row_num, [column_num], [area_num])
+    // NOTE: Please read the caveat above in binary search
+    pub(crate) fn fn_lookup(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() > 3 || args.len() < 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let target = self.evaluate_node_in_context(&args[0], cell);
+        if target.is_error() {
+            return target;
+        }
+        let value = self.evaluate_node_in_context(&args[1], cell);
+        match value {
+            CalcResult::Range { left, right } => {
+                let is_row_vector;
+                if left.row == right.row {
+                    is_row_vector = false;
+                } else if left.column == right.column {
+                    is_row_vector = true;
+                } else {
+                    // second argument must be a vector
+                    return CalcResult::Error {
+                        error: Error::ERROR,
+                        origin: cell,
+                        message: "Second argument must be a vector".to_string(),
+                    };
+                }
+                let l = self.binary_search(&target, &left, &right, is_row_vector);
+                if l == -2 {
+                    return CalcResult::Error {
+                        error: Error::NA,
+                        origin: cell,
+                        message: "Not found".to_string(),
+                    };
+                }
+
+                if args.len() == 3 {
+                    let target_range = self.evaluate_node_in_context(&args[2], cell);
+                    match target_range {
+                        CalcResult::Range {
+                            left: l1,
+                            right: _r1,
+                        } => {
+                            let row;
+                            let column;
+                            if is_row_vector {
+                                row = l1.row + l;
+                                column = l1.column;
+                            } else {
+                                column = l1.column + l;
+                                row = l1.row;
+                            }
+                            self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            })
+                        }
+                        error @ CalcResult::Error { .. } => error,
+                        _ => CalcResult::Error {
+                            error: Error::NA,
+                            origin: cell,
+                            message: "Range expected".to_string(),
+                        },
+                    }
+                } else {
+                    let row;
+                    let column;
+                    if is_row_vector {
+                        row = left.row + l;
+                        column = left.column;
+                    } else {
+                        column = left.column + l;
+                        row = left.row;
+                    }
+                    self.evaluate_cell(CellReference {
+                        sheet: left.sheet,
+                        row,
+                        column,
+                    })
+                }
+            }
+            error @ CalcResult::Error { .. } => error,
+            _ => CalcResult::Error {
+                error: Error::NA,
+                origin: cell,
+                message: "Range expected".to_string(),
+            },
+        }
+    }
+
+    // ROW([reference])
+    // If reference is not present returns the row of the present cell.
+    // Otherwise returns the row number of reference
+    pub(crate) fn fn_row(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() > 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        if args.is_empty() {
+            return CalcResult::Number(cell.row as f64);
+        }
+        match self.get_reference(&args[0], cell) {
+            Ok(c) => CalcResult::Number(c.left.row as f64),
+            Err(s) => s,
+        }
+    }
+
+    // ROWS(range)
+    // Returns the number of rows in range
+    pub(crate) fn fn_rows(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        match self.get_reference(&args[0], cell) {
+            Ok(c) => CalcResult::Number((c.right.row - c.left.row + 1) as f64),
+            Err(s) => s,
+        }
+    }
+
+    // COLUMN([reference])
+    // If reference is not present returns the column of the present cell.
+    // Otherwise returns the column number of reference
+    pub(crate) fn fn_column(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() > 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        if args.is_empty() {
+            return CalcResult::Number(cell.column as f64);
+        }
+
+        match self.get_reference(&args[0], cell) {
+            Ok(range) => CalcResult::Number(range.left.column as f64),
+            Err(s) => s,
+        }
+    }
+
+    /// CHOOSE(index_num, value1, [value2], ...)
+    /// Uses index_num to return a value from the list of value arguments.
+    pub(crate) fn fn_choose(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() < 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+
+        let index_num = match self.get_number(&args[0], cell) {
+            Ok(index_num) => index_num as usize,
+            Err(calc_err) => return calc_err,
+        };
+
+        if index_num < 1 || index_num > (args.len() - 1) {
+            return CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "Invalid index".to_string(),
+            };
+        }
+
+        self.evaluate_node_with_reference(&args[index_num], cell)
+    }
+
+    // COLUMNS(range)
+    // Returns the number of columns in range
+    pub(crate) fn fn_columns(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        match self.get_reference(&args[0], cell) {
+            Ok(c) => CalcResult::Number((c.right.column - c.left.column + 1) as f64),
+            Err(s) => s,
+        }
+    }
+
+    // INDIRECT(ref_tex)
+    // Returns the reference specified by 'ref_text'
+    pub(crate) fn fn_indirect(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() > 2 || args.is_empty() {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = self.get_string(&args[0], cell);
+        match value {
+            Ok(s) => {
+                if args.len() == 2 {
+                    return CalcResult::Error {
+                        error: Error::NIMPL,
+                        origin: cell,
+                        message: "Not implemented".to_string(),
+                    };
+                }
+
+                let parsed_reference = ParsedReference::parse_reference_formula(
+                    Some(cell.sheet),
+                    &s,
+                    &self.locale,
+                    |name| self.get_sheet_index_by_name(name),
+                );
+
+                let parsed_reference = match parsed_reference {
+                    Ok(reference) => reference,
+                    Err(message) => {
+                        return CalcResult::Error {
+                            error: Error::REF,
+                            origin: cell,
+                            message,
+                        };
+                    }
+                };
+
+                match parsed_reference {
+                    ParsedReference::CellReference(reference) => CalcResult::Range {
+                        left: reference,
+                        right: reference,
+                    },
+                    ParsedReference::Range(left, right) => CalcResult::Range { left, right },
+                }
+            }
+            Err(v) => v,
+        }
+    }
+
+    // OFFSET(reference, rows, cols, [height], [width])
+    // Returns a reference to a range that is a specified number of rows and columns from a cell or range of cells.
+    // The reference that is returned can be a single cell or a range of cells.
+    // You can specify the number of rows and the number of columns to be returned.
+    pub(crate) fn fn_offset(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let l = args.len();
+        if !(3..=5).contains(&l) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let reference = match self.get_reference(&args[0], cell) {
+            Ok(c) => c,
+            Err(s) => return s,
+        };
+        let rows = match self.get_number(&args[1], cell) {
+            Ok(c) => {
+                if c < 0.0 {
+                    c.ceil() as i32
+                } else {
+                    c.floor() as i32
+                }
+            }
+            Err(s) => return s,
+        };
+        let cols = match self.get_number(&args[2], cell) {
+            Ok(c) => {
+                if c < 0.0 {
+                    c.ceil() as i32
+                } else {
+                    c.floor() as i32
+                }
+            }
+            Err(s) => return s,
+        };
+        let row_start = reference.left.row + rows;
+        let column_start = reference.left.column + cols;
+        let width;
+        let height;
+        if l == 4 {
+            height = match self.get_number(&args[3], cell) {
+                Ok(c) => {
+                    if c < 1.0 {
+                        c.ceil() as i32 - 1
+                    } else {
+                        c.floor() as i32 - 1
+                    }
+                }
+                Err(s) => return s,
+            };
+            width = reference.right.column - reference.left.column;
+        } else if l == 5 {
+            height = match self.get_number(&args[3], cell) {
+                Ok(c) => {
+                    if c < 1.0 {
+                        c.ceil() as i32 - 1
+                    } else {
+                        c.floor() as i32 - 1
+                    }
+                }
+                Err(s) => return s,
+            };
+            width = match self.get_number(&args[4], cell) {
+                Ok(c) => {
+                    if c < 1.0 {
+                        c.ceil() as i32 - 1
+                    } else {
+                        c.floor() as i32 - 1
+                    }
+                }
+                Err(s) => return s,
+            };
+        } else {
+            width = reference.right.column - reference.left.column;
+            height = reference.right.row - reference.left.row;
+        }
+        // This is what Excel does
+        if width == -1 || height == -1 {
+            return CalcResult::Error {
+                error: Error::REF,
+                origin: cell,
+                message: "Invalid reference".to_string(),
+            };
+        }
+        // NB: Excel documentation says that negative values of width and height are not valid
+        // but in practice they are valid. We follow the documentation and not Excel
+        if width < -1 || height < -1 {
+            return CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "width and height cannot be negative".to_string(),
+            };
+        }
+
+        let column_end = column_start + width;
+        let row_end = row_start + height;
+        if row_start < 1 || row_end > LAST_ROW || column_start < 1 || column_end > LAST_COLUMN {
+            return CalcResult::Error {
+                error: Error::REF,
+                origin: cell,
+                message: "Invalid reference".to_string(),
+            };
+        }
+        let left = CellReference {
+            sheet: reference.left.sheet,
+            row: row_start,
+            column: column_start,
+        };
+        let right = CellReference {
+            sheet: reference.right.sheet,
+            row: row_end,
+            column: column_end,
+        };
+        CalcResult::Range { left, right }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/mathematical.rs.html b/src/ironcalc_base/functions/mathematical.rs.html new file mode 100644 index 0000000..ae156e3 --- /dev/null +++ b/src/ironcalc_base/functions/mathematical.rs.html @@ -0,0 +1,1343 @@ +mathematical.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+
use crate::constants::{LAST_COLUMN, LAST_ROW};
+use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::parser::Node,
+    expressions::token::Error,
+    model::Model,
+};
+use std::f64::consts::PI;
+
+#[cfg(not(target_arch = "wasm32"))]
+pub fn random() -> f64 {
+    rand::random()
+}
+
+#[cfg(target_arch = "wasm32")]
+pub fn random() -> f64 {
+    use js_sys::Math;
+    Math::random()
+}
+
+impl Model {
+    pub(crate) fn fn_min(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let mut result = f64::NAN;
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::Number(value) => result = value.min(result),
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    for row in left.row..(right.row + 1) {
+                        for column in left.column..(right.column + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::Number(value) => {
+                                    result = value.min(result);
+                                }
+                                error @ CalcResult::Error { .. } => return error,
+                                _ => {
+                                    // We ignore booleans and strings
+                                }
+                            }
+                        }
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                _ => {
+                    // We ignore booleans and strings
+                }
+            };
+        }
+        if result.is_nan() || result.is_infinite() {
+            return CalcResult::Number(0.0);
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_max(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let mut result = f64::NAN;
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::Number(value) => result = value.max(result),
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    for row in left.row..(right.row + 1) {
+                        for column in left.column..(right.column + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::Number(value) => {
+                                    result = value.max(result);
+                                }
+                                error @ CalcResult::Error { .. } => return error,
+                                _ => {
+                                    // We ignore booleans and strings
+                                }
+                            }
+                        }
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                _ => {
+                    // We ignore booleans and strings
+                }
+            };
+        }
+        if result.is_nan() || result.is_infinite() {
+            return CalcResult::Number(0.0);
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_sum(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.is_empty() {
+            return CalcResult::new_args_number_error(cell);
+        }
+
+        let mut result = 0.0;
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::Number(value) => result += value,
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    // TODO: We should do this for all functions that run through ranges
+                    // Running cargo test for the ironcalc takes around .8 seconds with this speedup
+                    // and ~ 3.5 seconds without it. Note that once properly in place sheet.dimension should be almost a noop
+                    let row1 = left.row;
+                    let mut row2 = right.row;
+                    let column1 = left.column;
+                    let mut column2 = right.column;
+                    if row1 == 1 && row2 == LAST_ROW {
+                        row2 = self
+                            .workbook
+                            .worksheet(left.sheet)
+                            .expect("Sheet expected during evaluation.")
+                            .dimension()
+                            .max_row;
+                    }
+                    if column1 == 1 && column2 == LAST_COLUMN {
+                        column2 = self
+                            .workbook
+                            .worksheet(left.sheet)
+                            .expect("Sheet expected during evaluation.")
+                            .dimension()
+                            .max_column;
+                    }
+                    for row in row1..row2 + 1 {
+                        for column in column1..(column2 + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::Number(value) => {
+                                    result += value;
+                                }
+                                error @ CalcResult::Error { .. } => return error,
+                                _ => {
+                                    // We ignore booleans and strings
+                                }
+                            }
+                        }
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                _ => {
+                    // We ignore booleans and strings
+                }
+            };
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_product(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.is_empty() {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let mut result = 1.0;
+        let mut seen_value = false;
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::Number(value) => {
+                    seen_value = true;
+                    result *= value;
+                }
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    let row1 = left.row;
+                    let mut row2 = right.row;
+                    let column1 = left.column;
+                    let mut column2 = right.column;
+                    if row1 == 1 && row2 == LAST_ROW {
+                        row2 = self
+                            .workbook
+                            .worksheet(left.sheet)
+                            .expect("Sheet expected during evaluation.")
+                            .dimension()
+                            .max_row;
+                    }
+                    if column1 == 1 && column2 == LAST_COLUMN {
+                        column2 = self
+                            .workbook
+                            .worksheet(left.sheet)
+                            .expect("Sheet expected during evaluation.")
+                            .dimension()
+                            .max_column;
+                    }
+                    for row in row1..row2 + 1 {
+                        for column in column1..(column2 + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::Number(value) => {
+                                    seen_value = true;
+                                    result *= value;
+                                }
+                                error @ CalcResult::Error { .. } => return error,
+                                _ => {
+                                    // We ignore booleans and strings
+                                }
+                            }
+                        }
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                _ => {
+                    // We ignore booleans and strings
+                }
+            };
+        }
+        if !seen_value {
+            return CalcResult::Number(0.0);
+        }
+        CalcResult::Number(result)
+    }
+
+    /// SUMIF(criteria_range, criteria, [sum_range])
+    /// if sum_rage is missing then criteria_range will be used
+    pub(crate) fn fn_sumif(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 2 {
+            let arguments = vec![args[0].clone(), args[0].clone(), args[1].clone()];
+            self.fn_sumifs(&arguments, cell)
+        } else if args.len() == 3 {
+            let arguments = vec![args[2].clone(), args[0].clone(), args[1].clone()];
+            self.fn_sumifs(&arguments, cell)
+        } else {
+            CalcResult::new_args_number_error(cell)
+        }
+    }
+
+    /// SUMIFS(sum_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
+    pub(crate) fn fn_sumifs(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let mut total = 0.0;
+        let sum = |value| total += value;
+        if let Err(e) = self.apply_ifs(args, cell, sum) {
+            return e;
+        }
+        CalcResult::Number(total)
+    }
+
+    pub(crate) fn fn_round(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            // Incorrect number of arguments
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let number_of_digits = match self.get_number(&args[1], cell) {
+            Ok(f) => {
+                if f > 0.0 {
+                    f.floor()
+                } else {
+                    f.ceil()
+                }
+            }
+            Err(s) => return s,
+        };
+        let scale = 10.0_f64.powf(number_of_digits);
+        CalcResult::Number((value * scale).round() / scale)
+    }
+    pub(crate) fn fn_roundup(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let number_of_digits = match self.get_number(&args[1], cell) {
+            Ok(f) => {
+                if f > 0.0 {
+                    f.floor()
+                } else {
+                    f.ceil()
+                }
+            }
+            Err(s) => return s,
+        };
+        let scale = 10.0_f64.powf(number_of_digits);
+        if value > 0.0 {
+            CalcResult::Number((value * scale).ceil() / scale)
+        } else {
+            CalcResult::Number((value * scale).floor() / scale)
+        }
+    }
+    pub(crate) fn fn_rounddown(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let number_of_digits = match self.get_number(&args[1], cell) {
+            Ok(f) => {
+                if f > 0.0 {
+                    f.floor()
+                } else {
+                    f.ceil()
+                }
+            }
+            Err(s) => return s,
+        };
+        let scale = 10.0_f64.powf(number_of_digits);
+        if value > 0.0 {
+            CalcResult::Number((value * scale).floor() / scale)
+        } else {
+            CalcResult::Number((value * scale).ceil() / scale)
+        }
+    }
+
+    pub(crate) fn fn_sin(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let result = value.sin();
+        CalcResult::Number(result)
+    }
+    pub(crate) fn fn_cos(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let result = value.cos();
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_tan(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let result = value.tan();
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_sinh(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let result = value.sinh();
+        CalcResult::Number(result)
+    }
+    pub(crate) fn fn_cosh(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let result = value.cosh();
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_tanh(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let result = value.tanh();
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_asin(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let result = value.asin();
+        if result.is_nan() || result.is_infinite() {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid argument for ASIN".to_string(),
+            };
+        }
+        CalcResult::Number(result)
+    }
+    pub(crate) fn fn_acos(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let result = value.acos();
+        if result.is_nan() || result.is_infinite() {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid argument for COS".to_string(),
+            };
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_atan(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let result = value.atan();
+        if result.is_nan() || result.is_infinite() {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid argument for ATAN".to_string(),
+            };
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_asinh(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let result = value.asinh();
+        if result.is_nan() || result.is_infinite() {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid argument for ASINH".to_string(),
+            };
+        }
+        CalcResult::Number(result)
+    }
+    pub(crate) fn fn_acosh(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let result = value.acosh();
+        if result.is_nan() || result.is_infinite() {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid argument for ACOSH".to_string(),
+            };
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_atanh(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let result = value.atanh();
+        if result.is_nan() || result.is_infinite() {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid argument for ATANH".to_string(),
+            };
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_pi(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !args.is_empty() {
+            return CalcResult::new_args_number_error(cell);
+        }
+        CalcResult::Number(PI)
+    }
+
+    pub(crate) fn fn_abs(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        CalcResult::Number(value.abs())
+    }
+
+    pub(crate) fn fn_sqrtpi(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if value < 0.0 {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Argument of SQRTPI should be >= 0".to_string(),
+            };
+        }
+        CalcResult::Number((value * PI).sqrt())
+    }
+
+    pub(crate) fn fn_sqrt(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if value < 0.0 {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Argument of SQRT should be >= 0".to_string(),
+            };
+        }
+        CalcResult::Number(value.sqrt())
+    }
+
+    pub(crate) fn fn_atan2(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let x = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let y = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if x == 0.0 && y == 0.0 {
+            return CalcResult::Error {
+                error: Error::DIV,
+                origin: cell,
+                message: "Arguments can't be both zero".to_string(),
+            };
+        }
+        CalcResult::Number(f64::atan2(y, x))
+    }
+
+    pub(crate) fn fn_power(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let x = match self.get_number(&args[0], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        let y = match self.get_number(&args[1], cell) {
+            Ok(f) => f,
+            Err(s) => return s,
+        };
+        if x == 0.0 && y == 0.0 {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Arguments can't be both zero".to_string(),
+            };
+        }
+        if y == 0.0 {
+            return CalcResult::Number(1.0);
+        }
+        let result = x.powf(y);
+        if result.is_infinite() {
+            return CalcResult::Error {
+                error: Error::DIV,
+                origin: cell,
+                message: "POWER returned infinity".to_string(),
+            };
+        }
+        if result.is_nan() {
+            // This might happen for some combinations of negative base and exponent
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: "Invalid arguments for POWER".to_string(),
+            };
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_rand(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if !args.is_empty() {
+            return CalcResult::new_args_number_error(cell);
+        }
+        CalcResult::Number(random())
+    }
+
+    // TODO: Add tests for RANDBETWEEN
+    pub(crate) fn fn_randbetween(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let x = match self.get_number(&args[0], cell) {
+            Ok(f) => f.floor(),
+            Err(s) => return s,
+        };
+        let y = match self.get_number(&args[1], cell) {
+            Ok(f) => f.ceil() + 1.0,
+            Err(s) => return s,
+        };
+        if x > y {
+            return CalcResult::Error {
+                error: Error::NUM,
+                origin: cell,
+                message: format!("{x}>{y}"),
+            };
+        }
+        CalcResult::Number((x + random() * (y - x)).floor())
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/mod.rs.html b/src/ironcalc_base/functions/mod.rs.html new file mode 100644 index 0000000..9675154 --- /dev/null +++ b/src/ironcalc_base/functions/mod.rs.html @@ -0,0 +1,1861 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+
use core::fmt;
+
+use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::{parser::Node, token::Error},
+    model::Model,
+};
+
+pub(crate) mod binary_search;
+mod date_and_time;
+mod engineering;
+mod financial;
+mod financial_util;
+mod information;
+mod logical;
+mod lookup_and_reference;
+mod mathematical;
+mod statistical;
+mod subtotal;
+mod text;
+mod text_util;
+pub(crate) mod util;
+mod xlookup;
+
+/// List of all implemented functions
+#[derive(PartialEq, Clone, Debug)]
+pub enum Function {
+    // Logical
+    And,
+    False,
+    If,
+    Iferror,
+    Ifna,
+    Ifs,
+    Not,
+    Or,
+    Switch,
+    True,
+    Xor,
+
+    // Mathematical and trigonometry
+    Abs,
+    Acos,
+    Acosh,
+    Asin,
+    Asinh,
+    Atan,
+    Atan2,
+    Atanh,
+    Choose,
+    Column,
+    Columns,
+    Cos,
+    Cosh,
+    Max,
+    Min,
+    Pi,
+    Power,
+    Product,
+    Rand,
+    Randbetween,
+    Round,
+    Rounddown,
+    Roundup,
+    Sin,
+    Sinh,
+    Sqrt,
+    Sqrtpi,
+    Sum,
+    Sumif,
+    Sumifs,
+    Tan,
+    Tanh,
+
+    // Information
+    ErrorType,
+    Isblank,
+    Iserr,
+    Iserror,
+    Iseven,
+    Isformula,
+    Islogical,
+    Isna,
+    Isnontext,
+    Isnumber,
+    Isodd,
+    Isref,
+    Istext,
+    Na,
+    Sheet,
+    Type,
+
+    // Lookup and reference
+    Hlookup,
+    Index,
+    Indirect,
+    Lookup,
+    Match,
+    Offset,
+    Row,
+    Rows,
+    Vlookup,
+    Xlookup,
+
+    // Text
+    Concat,
+    Concatenate,
+    Exact,
+    Find,
+    Left,
+    Len,
+    Lower,
+    Mid,
+    Rept,
+    Right,
+    Search,
+    Substitute,
+    T,
+    Text,
+    Textafter,
+    Textbefore,
+    Textjoin,
+    Trim,
+    Upper,
+    Value,
+    Valuetotext,
+
+    // Statistical
+    Average,
+    Averagea,
+    Averageif,
+    Averageifs,
+    Count,
+    Counta,
+    Countblank,
+    Countif,
+    Countifs,
+    Maxifs,
+    Minifs,
+
+    // Date and time
+    Date,
+    Day,
+    Edate,
+    Eomonth,
+    Month,
+    Now,
+    Today,
+    Year,
+
+    // Financial
+    Cumipmt,
+    Cumprinc,
+    Db,
+    Ddb,
+    Dollarde,
+    Dollarfr,
+    Effect,
+    Fv,
+    Ipmt,
+    Irr,
+    Ispmt,
+    Mirr,
+    Nominal,
+    Nper,
+    Npv,
+    Pduration,
+    Pmt,
+    Ppmt,
+    Pv,
+    Rate,
+    Rri,
+    Sln,
+    Syd,
+    Tbilleq,
+    Tbillprice,
+    Tbillyield,
+    Xirr,
+    Xnpv,
+
+    // Engineering: Bessel and transcendental functions
+    Besseli,
+    Besselj,
+    Besselk,
+    Bessely,
+    Erf,
+    Erfc,
+    ErfcPrecise,
+    ErfPrecise,
+
+    // Engineering: Number systems
+    Bin2dec,
+    Bin2hex,
+    Bin2oct,
+    Dec2Bin,
+    Dec2hex,
+    Dec2oct,
+    Hex2bin,
+    Hex2dec,
+    Hex2oct,
+    Oct2bin,
+    Oct2dec,
+    Oct2hex,
+
+    // Engineering: Bit functions
+    Bitand,
+    Bitlshift,
+    Bitor,
+    Bitrshift,
+    Bitxor,
+
+    // Engineering: Complex functions
+    Complex,
+    Imabs,
+    Imaginary,
+    Imargument,
+    Imconjugate,
+    Imcos,
+    Imcosh,
+    Imcot,
+    Imcsc,
+    Imcsch,
+    Imdiv,
+    Imexp,
+    Imln,
+    Imlog10,
+    Imlog2,
+    Impower,
+    Improduct,
+    Imreal,
+    Imsec,
+    Imsech,
+    Imsin,
+    Imsinh,
+    Imsqrt,
+    Imsub,
+    Imsum,
+    Imtan,
+
+    // Engineering: Misc function
+    Convert,
+    Delta,
+    Gestep,
+    Subtotal,
+}
+
+impl Function {
+    /// Some functions in Excel like CONCAT are stringified as `_xlfn.CONCAT`.
+    pub fn to_xlsx_string(&self) -> String {
+        match self {
+            Function::Concat => "_xlfn.CONCAT".to_string(),
+            Function::Ifna => "_xlfn.IFNA".to_string(),
+            Function::Ifs => "_xlfn.IFS".to_string(),
+            Function::Maxifs => "_xlfn.MAXIFS".to_string(),
+            Function::Minifs => "_xlfn.MINIFS".to_string(),
+            Function::Switch => "_xlfn.SWITCH".to_string(),
+            Function::Xlookup => "_xlfn.XLOOKUP".to_string(),
+            Function::Xor => "_xlfn.XOR".to_string(),
+            Function::Textbefore => "_xlfn.TEXTBEFORE".to_string(),
+            Function::Textafter => "_xlfn.TEXTAFTER".to_string(),
+            Function::Textjoin => "_xlfn.TEXTJOIN".to_string(),
+            Function::Rri => "_xlfn.RRI".to_string(),
+            Function::Pduration => "_xlfn.PDURATION".to_string(),
+            Function::Bitand => "_xlfn.BITAND".to_string(),
+            Function::Bitor => "_xlfn.BITOR".to_string(),
+            Function::Bitxor => "_xlfn.BITXOR".to_string(),
+            Function::Bitlshift => "_xlfn.BITLSHIFT".to_string(),
+            Function::Bitrshift => "_xlfn.BITRSHIFT".to_string(),
+            Function::Imtan => "_xlfn.IMTAN".to_string(),
+            Function::Imsinh => "_xlfn.IMSINH".to_string(),
+            Function::Imcosh => "_xlfn.IMCOSH".to_string(),
+            Function::Imcot => "_xlfn.IMCOT".to_string(),
+            Function::Imcsc => "_xlfn.IMCSC".to_string(),
+            Function::Imcsch => "_xlfn.IMCSCH".to_string(),
+            Function::Imsec => "_xlfn.IMSEC".to_string(),
+            Function::ErfcPrecise => "_xlfn.ERFC.PRECISE".to_string(),
+            Function::ErfPrecise => "_xlfn.ERF.PRECISE".to_string(),
+            Function::Valuetotext => "_xlfn.VALUETOTEXT".to_string(),
+            Function::Isformula => "_xlfn.ISFORMULA".to_string(),
+            Function::Sheet => "_xlfn.SHEET".to_string(),
+            _ => self.to_string(),
+        }
+    }
+
+    pub(crate) fn returns_reference(&self) -> bool {
+        matches!(self, Function::Indirect | Function::Offset)
+    }
+    /// Gets the function from the name.
+    /// Note that in Excel some (modern) functions are prefixed by `_xlfn.`
+    pub fn get_function(name: &str) -> Option<Function> {
+        match name.to_ascii_uppercase().as_str() {
+            "AND" => Some(Function::And),
+            "FALSE" => Some(Function::False),
+            "IF" => Some(Function::If),
+            "IFERROR" => Some(Function::Iferror),
+            "IFNA" | "_XLFN.IFNA" => Some(Function::Ifna),
+            "IFS" | "_XLFN.IFS" => Some(Function::Ifs),
+            "NOT" => Some(Function::Not),
+            "OR" => Some(Function::Or),
+            "SWITCH" | "_XLFN.SWITCH" => Some(Function::Switch),
+            "TRUE" => Some(Function::True),
+            "XOR" | "_XLFN.XOR" => Some(Function::Xor),
+
+            "SIN" => Some(Function::Sin),
+            "COS" => Some(Function::Cos),
+            "TAN" => Some(Function::Tan),
+
+            "ASIN" => Some(Function::Asin),
+            "ACOS" => Some(Function::Acos),
+            "ATAN" => Some(Function::Atan),
+
+            "SINH" => Some(Function::Sinh),
+            "COSH" => Some(Function::Cosh),
+            "TANH" => Some(Function::Tanh),
+
+            "ASINH" => Some(Function::Asinh),
+            "ACOSH" => Some(Function::Acosh),
+            "ATANH" => Some(Function::Atanh),
+
+            "PI" => Some(Function::Pi),
+            "ABS" => Some(Function::Abs),
+            "SQRT" => Some(Function::Sqrt),
+            "SQRTPI" => Some(Function::Sqrtpi),
+            "POWER" => Some(Function::Power),
+            "ATAN2" => Some(Function::Atan2),
+
+            "MAX" => Some(Function::Max),
+            "MIN" => Some(Function::Min),
+            "PRODUCT" => Some(Function::Product),
+            "RAND" => Some(Function::Rand),
+            "RANDBETWEEN" => Some(Function::Randbetween),
+            "ROUND" => Some(Function::Round),
+            "ROUNDDOWN" => Some(Function::Rounddown),
+            "ROUNDUP" => Some(Function::Roundup),
+            "SUM" => Some(Function::Sum),
+            "SUMIF" => Some(Function::Sumif),
+            "SUMIFS" => Some(Function::Sumifs),
+
+            // Lookup and Reference
+            "CHOOSE" => Some(Function::Choose),
+            "COLUMN" => Some(Function::Column),
+            "COLUMNS" => Some(Function::Columns),
+            "INDEX" => Some(Function::Index),
+            "INDIRECT" => Some(Function::Indirect),
+            "HLOOKUP" => Some(Function::Hlookup),
+            "LOOKUP" => Some(Function::Lookup),
+            "MATCH" => Some(Function::Match),
+            "OFFSET" => Some(Function::Offset),
+            "ROW" => Some(Function::Row),
+            "ROWS" => Some(Function::Rows),
+            "VLOOKUP" => Some(Function::Vlookup),
+            "XLOOKUP" | "_XLFN.XLOOKUP" => Some(Function::Xlookup),
+
+            "CONCATENATE" => Some(Function::Concatenate),
+            "EXACT" => Some(Function::Exact),
+            "VALUE" => Some(Function::Value),
+            "T" => Some(Function::T),
+            "VALUETOTEXT" | "_XLFN.VALUETOTEXT" => Some(Function::Valuetotext),
+            "CONCAT" | "_XLFN.CONCAT" => Some(Function::Concat),
+            "FIND" => Some(Function::Find),
+            "LEFT" => Some(Function::Left),
+            "LEN" => Some(Function::Len),
+            "LOWER" => Some(Function::Lower),
+            "MID" => Some(Function::Mid),
+            "RIGHT" => Some(Function::Right),
+            "SEARCH" => Some(Function::Search),
+            "TEXT" => Some(Function::Text),
+            "TRIM" => Some(Function::Trim),
+            "UPPER" => Some(Function::Upper),
+
+            "REPT" => Some(Function::Rept),
+            "TEXTAFTER" | "_XLFN.TEXTAFTER" => Some(Function::Textafter),
+            "TEXTBEFORE" | "_XLFN.TEXTBEFORE" => Some(Function::Textbefore),
+            "TEXTJOIN" | "_XLFN.TEXTJOIN" => Some(Function::Textjoin),
+            "SUBSTITUTE" => Some(Function::Substitute),
+
+            "ISNUMBER" => Some(Function::Isnumber),
+            "ISNONTEXT" => Some(Function::Isnontext),
+            "ISTEXT" => Some(Function::Istext),
+            "ISLOGICAL" => Some(Function::Islogical),
+            "ISBLANK" => Some(Function::Isblank),
+            "ISERR" => Some(Function::Iserr),
+            "ISERROR" => Some(Function::Iserror),
+            "ISNA" => Some(Function::Isna),
+            "NA" => Some(Function::Na),
+            "ISREF" => Some(Function::Isref),
+            "ISODD" => Some(Function::Isodd),
+            "ISEVEN" => Some(Function::Iseven),
+            "ERROR.TYPE" => Some(Function::ErrorType),
+            "ISFORMULA" | "_XLFN.ISFORMULA" => Some(Function::Isformula),
+            "TYPE" => Some(Function::Type),
+            "SHEET" | "_XLFN.SHEET" => Some(Function::Sheet),
+
+            "AVERAGE" => Some(Function::Average),
+            "AVERAGEA" => Some(Function::Averagea),
+            "AVERAGEIF" => Some(Function::Averageif),
+            "AVERAGEIFS" => Some(Function::Averageifs),
+            "COUNT" => Some(Function::Count),
+            "COUNTA" => Some(Function::Counta),
+            "COUNTBLANK" => Some(Function::Countblank),
+            "COUNTIF" => Some(Function::Countif),
+            "COUNTIFS" => Some(Function::Countifs),
+            "MAXIFS" | "_XLFN.MAXIFS" => Some(Function::Maxifs),
+            "MINIFS" | "_XLFN.MINIFS" => Some(Function::Minifs),
+            // Date and Time
+            "YEAR" => Some(Function::Year),
+            "DAY" => Some(Function::Day),
+            "EOMONTH" => Some(Function::Eomonth),
+            "MONTH" => Some(Function::Month),
+            "DATE" => Some(Function::Date),
+            "EDATE" => Some(Function::Edate),
+            "TODAY" => Some(Function::Today),
+            "NOW" => Some(Function::Now),
+            // Financial
+            "PMT" => Some(Function::Pmt),
+            "PV" => Some(Function::Pv),
+            "RATE" => Some(Function::Rate),
+            "NPER" => Some(Function::Nper),
+            "FV" => Some(Function::Fv),
+            "PPMT" => Some(Function::Ppmt),
+            "IPMT" => Some(Function::Ipmt),
+            "NPV" => Some(Function::Npv),
+            "XNPV" => Some(Function::Xnpv),
+            "MIRR" => Some(Function::Mirr),
+            "IRR" => Some(Function::Irr),
+            "XIRR" => Some(Function::Xirr),
+            "ISPMT" => Some(Function::Ispmt),
+            "RRI" | "_XLFN.RRI" => Some(Function::Rri),
+
+            "SLN" => Some(Function::Sln),
+            "SYD" => Some(Function::Syd),
+            "NOMINAL" => Some(Function::Nominal),
+            "EFFECT" => Some(Function::Effect),
+            "PDURATION" | "_XLFN.PDURATION" => Some(Function::Pduration),
+
+            "TBILLYIELD" => Some(Function::Tbillyield),
+            "TBILLPRICE" => Some(Function::Tbillprice),
+            "TBILLEQ" => Some(Function::Tbilleq),
+
+            "DOLLARDE" => Some(Function::Dollarde),
+            "DOLLARFR" => Some(Function::Dollarfr),
+
+            "DDB" => Some(Function::Ddb),
+            "DB" => Some(Function::Db),
+
+            "CUMPRINC" => Some(Function::Cumprinc),
+            "CUMIPMT" => Some(Function::Cumipmt),
+
+            "BESSELI" => Some(Function::Besseli),
+            "BESSELJ" => Some(Function::Besselj),
+            "BESSELK" => Some(Function::Besselk),
+            "BESSELY" => Some(Function::Bessely),
+            "ERF" => Some(Function::Erf),
+            "ERF.PRECISE" | "_XLFN.ERF.PRECISE" => Some(Function::ErfPrecise),
+            "ERFC" => Some(Function::Erfc),
+            "ERFC.PRECISE" | "_XLFN.ERFC.PRECISE" => Some(Function::ErfcPrecise),
+            "BIN2DEC" => Some(Function::Bin2dec),
+            "BIN2HEX" => Some(Function::Bin2hex),
+            "BIN2OCT" => Some(Function::Bin2oct),
+            "DEC2BIN" => Some(Function::Dec2Bin),
+            "DEC2HEX" => Some(Function::Dec2hex),
+            "DEC2OCT" => Some(Function::Dec2oct),
+            "HEX2BIN" => Some(Function::Hex2bin),
+            "HEX2DEC" => Some(Function::Hex2dec),
+            "HEX2OCT" => Some(Function::Hex2oct),
+            "OCT2BIN" => Some(Function::Oct2bin),
+            "OCT2DEC" => Some(Function::Oct2dec),
+            "OCT2HEX" => Some(Function::Oct2hex),
+            "BITAND" | "_XLFN.BITAND" => Some(Function::Bitand),
+            "BITLSHIFT" | "_XLFN.BITLSHIFT" => Some(Function::Bitlshift),
+            "BITOR" | "_XLFN.BITOR" => Some(Function::Bitor),
+            "BITRSHIFT" | "_XLFN.BITRSHIFT" => Some(Function::Bitrshift),
+            "BITXOR" | "_XLFN.BITXOR" => Some(Function::Bitxor),
+            "COMPLEX" => Some(Function::Complex),
+            "IMABS" => Some(Function::Imabs),
+            "IMAGINARY" => Some(Function::Imaginary),
+            "IMARGUMENT" => Some(Function::Imargument),
+            "IMCONJUGATE" => Some(Function::Imconjugate),
+            "IMCOS" => Some(Function::Imcos),
+            "IMCOSH" | "_XLFN.IMCOSH" => Some(Function::Imcosh),
+            "IMCOT" | "_XLFN.IMCOT" => Some(Function::Imcot),
+            "IMCSC" | "_XLFN.IMCSC" => Some(Function::Imcsc),
+            "IMCSCH" | "_XLFN.IMCSCH" => Some(Function::Imcsch),
+            "IMDIV" => Some(Function::Imdiv),
+            "IMEXP" => Some(Function::Imexp),
+            "IMLN" => Some(Function::Imln),
+            "IMLOG10" => Some(Function::Imlog10),
+            "IMLOG2" => Some(Function::Imlog2),
+            "IMPOWER" => Some(Function::Impower),
+            "IMPRODUCT" => Some(Function::Improduct),
+            "IMREAL" => Some(Function::Imreal),
+            "IMSEC" | "_XLFN.IMSEC" => Some(Function::Imsec),
+            "IMSECH" | "_XLFN.IMSECH" => Some(Function::Imsech),
+            "IMSIN" => Some(Function::Imsin),
+            "IMSINH" | "_XLFN.IMSINH" => Some(Function::Imsinh),
+            "IMSQRT" => Some(Function::Imsqrt),
+            "IMSUB" => Some(Function::Imsub),
+            "IMSUM" => Some(Function::Imsum),
+            "IMTAN" | "_XLFN.IMTAN" => Some(Function::Imtan),
+            "CONVERT" => Some(Function::Convert),
+            "DELTA" => Some(Function::Delta),
+            "GESTEP" => Some(Function::Gestep),
+
+            "SUBTOTAL" => Some(Function::Subtotal),
+            _ => None,
+        }
+    }
+}
+
+impl fmt::Display for Function {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Function::And => write!(f, "AND"),
+            Function::False => write!(f, "FALSE"),
+            Function::If => write!(f, "IF"),
+            Function::Iferror => write!(f, "IFERROR"),
+            Function::Ifna => write!(f, "IFNA"),
+            Function::Ifs => write!(f, "IFS"),
+            Function::Not => write!(f, "NOT"),
+            Function::Or => write!(f, "OR"),
+            Function::Switch => write!(f, "SWITCH"),
+            Function::True => write!(f, "TRUE"),
+            Function::Xor => write!(f, "XOR"),
+            Function::Sin => write!(f, "SIN"),
+            Function::Cos => write!(f, "COS"),
+            Function::Tan => write!(f, "TAN"),
+            Function::Asin => write!(f, "ASIN"),
+            Function::Acos => write!(f, "ACOS"),
+            Function::Atan => write!(f, "ATAN"),
+            Function::Sinh => write!(f, "SINH"),
+            Function::Cosh => write!(f, "COSH"),
+            Function::Tanh => write!(f, "TANH"),
+            Function::Asinh => write!(f, "ASINH"),
+            Function::Acosh => write!(f, "ACOSH"),
+            Function::Atanh => write!(f, "ATANH"),
+            Function::Abs => write!(f, "ABS"),
+            Function::Pi => write!(f, "PI"),
+            Function::Sqrt => write!(f, "SQRT"),
+            Function::Sqrtpi => write!(f, "SQRTPI"),
+            Function::Atan2 => write!(f, "ATAN2"),
+            Function::Power => write!(f, "POWER"),
+            Function::Max => write!(f, "MAX"),
+            Function::Min => write!(f, "MIN"),
+            Function::Product => write!(f, "PRODUCT"),
+            Function::Rand => write!(f, "RAND"),
+            Function::Randbetween => write!(f, "RANDBETWEEN"),
+            Function::Round => write!(f, "ROUND"),
+            Function::Rounddown => write!(f, "ROUNDDOWN"),
+            Function::Roundup => write!(f, "ROUNDUP"),
+            Function::Sum => write!(f, "SUM"),
+            Function::Sumif => write!(f, "SUMIF"),
+            Function::Sumifs => write!(f, "SUMIFS"),
+            Function::Choose => write!(f, "CHOOSE"),
+            Function::Column => write!(f, "COLUMN"),
+            Function::Columns => write!(f, "COLUMNS"),
+            Function::Index => write!(f, "INDEX"),
+            Function::Indirect => write!(f, "INDIRECT"),
+            Function::Hlookup => write!(f, "HLOOKUP"),
+            Function::Lookup => write!(f, "LOOKUP"),
+            Function::Match => write!(f, "MATCH"),
+            Function::Offset => write!(f, "OFFSET"),
+            Function::Row => write!(f, "ROW"),
+            Function::Rows => write!(f, "ROWS"),
+            Function::Vlookup => write!(f, "VLOOKUP"),
+            Function::Xlookup => write!(f, "XLOOKUP"),
+            Function::Concatenate => write!(f, "CONCATENATE"),
+            Function::Exact => write!(f, "EXACT"),
+            Function::Value => write!(f, "VALUE"),
+            Function::T => write!(f, "T"),
+            Function::Valuetotext => write!(f, "VALUETOTEXT"),
+            Function::Concat => write!(f, "CONCAT"),
+            Function::Find => write!(f, "FIND"),
+            Function::Left => write!(f, "LEFT"),
+            Function::Len => write!(f, "LEN"),
+            Function::Lower => write!(f, "LOWER"),
+            Function::Mid => write!(f, "MID"),
+            Function::Right => write!(f, "RIGHT"),
+            Function::Search => write!(f, "SEARCH"),
+            Function::Text => write!(f, "TEXT"),
+            Function::Trim => write!(f, "TRIM"),
+            Function::Upper => write!(f, "UPPER"),
+            Function::Isnumber => write!(f, "ISNUMBER"),
+            Function::Isnontext => write!(f, "ISNONTEXT"),
+            Function::Istext => write!(f, "ISTEXT"),
+            Function::Islogical => write!(f, "ISLOGICAL"),
+            Function::Isblank => write!(f, "ISBLANK"),
+            Function::Iserr => write!(f, "ISERR"),
+            Function::Iserror => write!(f, "ISERROR"),
+            Function::Isna => write!(f, "ISNA"),
+            Function::Na => write!(f, "NA"),
+            Function::Isref => write!(f, "ISREF"),
+            Function::Isodd => write!(f, "ISODD"),
+            Function::Iseven => write!(f, "ISEVEN"),
+            Function::ErrorType => write!(f, "ERROR.TYPE"),
+            Function::Isformula => write!(f, "ISFORMULA"),
+            Function::Type => write!(f, "TYPE"),
+            Function::Sheet => write!(f, "SHEET"),
+
+            Function::Average => write!(f, "AVERAGE"),
+            Function::Averagea => write!(f, "AVERAGEA"),
+            Function::Averageif => write!(f, "AVERAGEIF"),
+            Function::Averageifs => write!(f, "AVERAGEIFS"),
+            Function::Count => write!(f, "COUNT"),
+            Function::Counta => write!(f, "COUNTA"),
+            Function::Countblank => write!(f, "COUNTBLANK"),
+            Function::Countif => write!(f, "COUNTIF"),
+            Function::Countifs => write!(f, "COUNTIFS"),
+            Function::Maxifs => write!(f, "MAXIFS"),
+            Function::Minifs => write!(f, "MINIFS"),
+            Function::Year => write!(f, "YEAR"),
+            Function::Day => write!(f, "DAY"),
+            Function::Month => write!(f, "MONTH"),
+            Function::Eomonth => write!(f, "EOMONTH"),
+            Function::Date => write!(f, "DATE"),
+            Function::Edate => write!(f, "EDATE"),
+            Function::Today => write!(f, "TODAY"),
+            Function::Now => write!(f, "NOW"),
+            Function::Pmt => write!(f, "PMT"),
+            Function::Pv => write!(f, "PV"),
+            Function::Rate => write!(f, "RATE"),
+            Function::Nper => write!(f, "NPER"),
+            Function::Fv => write!(f, "FV"),
+            Function::Ppmt => write!(f, "PPMT"),
+            Function::Ipmt => write!(f, "IPMT"),
+            Function::Npv => write!(f, "NPV"),
+            Function::Mirr => write!(f, "MIRR"),
+            Function::Irr => write!(f, "IRR"),
+            Function::Xirr => write!(f, "XIRR"),
+            Function::Xnpv => write!(f, "XNPV"),
+            Function::Rept => write!(f, "REPT"),
+            Function::Textafter => write!(f, "TEXTAFTER"),
+            Function::Textbefore => write!(f, "TEXTBEFORE"),
+            Function::Textjoin => write!(f, "TEXTJOIN"),
+            Function::Substitute => write!(f, "SUBSTITUTE"),
+            Function::Ispmt => write!(f, "ISPMT"),
+            Function::Rri => write!(f, "RRI"),
+            Function::Sln => write!(f, "SLN"),
+            Function::Syd => write!(f, "SYD"),
+            Function::Nominal => write!(f, "NOMINAL"),
+            Function::Effect => write!(f, "EFFECT"),
+            Function::Pduration => write!(f, "PDURATION"),
+            Function::Tbillyield => write!(f, "TBILLYIELD"),
+            Function::Tbillprice => write!(f, "TBILLPRICE"),
+            Function::Tbilleq => write!(f, "TBILLEQ"),
+            Function::Dollarde => write!(f, "DOLLARDE"),
+            Function::Dollarfr => write!(f, "DOLLARFR"),
+            Function::Ddb => write!(f, "DDB"),
+            Function::Db => write!(f, "DB"),
+            Function::Cumprinc => write!(f, "CUMPRINC"),
+            Function::Cumipmt => write!(f, "CUMIPMT"),
+            Function::Besseli => write!(f, "BESSELI"),
+            Function::Besselj => write!(f, "BESSELJ"),
+            Function::Besselk => write!(f, "BESSELK"),
+            Function::Bessely => write!(f, "BESSELY"),
+            Function::Erf => write!(f, "ERF"),
+            Function::ErfPrecise => write!(f, "ERF.PRECISE"),
+            Function::Erfc => write!(f, "ERFC"),
+            Function::ErfcPrecise => write!(f, "ERFC.PRECISE"),
+            Function::Bin2dec => write!(f, "BIN2DEC"),
+            Function::Bin2hex => write!(f, "BIN2HEX"),
+            Function::Bin2oct => write!(f, "BIN2OCT"),
+            Function::Dec2Bin => write!(f, "DEC2BIN"),
+            Function::Dec2hex => write!(f, "DEC2HEX"),
+            Function::Dec2oct => write!(f, "DEC2OCT"),
+            Function::Hex2bin => write!(f, "HEX2BIN"),
+            Function::Hex2dec => write!(f, "HEX2DEC"),
+            Function::Hex2oct => write!(f, "HEX2OCT"),
+            Function::Oct2bin => write!(f, "OCT2BIN"),
+            Function::Oct2dec => write!(f, "OCT2DEC"),
+            Function::Oct2hex => write!(f, "OCT2HEX"),
+            Function::Bitand => write!(f, "BITAND"),
+            Function::Bitlshift => write!(f, "BITLSHIFT"),
+            Function::Bitor => write!(f, "BITOR"),
+            Function::Bitrshift => write!(f, "BITRSHIFT"),
+            Function::Bitxor => write!(f, "BITXOR"),
+            Function::Complex => write!(f, "COMPLEX"),
+            Function::Imabs => write!(f, "IMABS"),
+            Function::Imaginary => write!(f, "IMAGINARY"),
+            Function::Imargument => write!(f, "IMARGUMENT"),
+            Function::Imconjugate => write!(f, "IMCONJUGATE"),
+            Function::Imcos => write!(f, "IMCOS"),
+            Function::Imcosh => write!(f, "IMCOSH"),
+            Function::Imcot => write!(f, "IMCOT"),
+            Function::Imcsc => write!(f, "IMCSC"),
+            Function::Imcsch => write!(f, "IMCSCH"),
+            Function::Imdiv => write!(f, "IMDIV"),
+            Function::Imexp => write!(f, "IMEXP"),
+            Function::Imln => write!(f, "IMLN"),
+            Function::Imlog10 => write!(f, "IMLOG10"),
+            Function::Imlog2 => write!(f, "IMLOG2"),
+            Function::Impower => write!(f, "IMPOWER"),
+            Function::Improduct => write!(f, "IMPRODUCT"),
+            Function::Imreal => write!(f, "IMREAL"),
+            Function::Imsec => write!(f, "IMSEC"),
+            Function::Imsech => write!(f, "IMSECH"),
+            Function::Imsin => write!(f, "IMSIN"),
+            Function::Imsinh => write!(f, "IMSINH"),
+            Function::Imsqrt => write!(f, "IMSQRT"),
+            Function::Imsub => write!(f, "IMSUB"),
+            Function::Imsum => write!(f, "IMSUM"),
+            Function::Imtan => write!(f, "IMTAN"),
+            Function::Convert => write!(f, "CONVERT"),
+            Function::Delta => write!(f, "DELTA"),
+            Function::Gestep => write!(f, "GESTEP"),
+
+            Function::Subtotal => write!(f, "SUBTOTAL"),
+        }
+    }
+}
+
+impl Model {
+    pub(crate) fn evaluate_function(
+        &mut self,
+        kind: &Function,
+        args: &[Node],
+        cell: CellReference,
+    ) -> CalcResult {
+        match kind {
+            // Logical
+            Function::And => self.fn_and(args, cell),
+            Function::False => CalcResult::Boolean(false),
+            Function::If => self.fn_if(args, cell),
+            Function::Iferror => self.fn_iferror(args, cell),
+            Function::Ifna => self.fn_ifna(args, cell),
+            Function::Ifs => self.fn_ifs(args, cell),
+            Function::Not => self.fn_not(args, cell),
+            Function::Or => self.fn_or(args, cell),
+            Function::Switch => self.fn_switch(args, cell),
+            Function::True => CalcResult::Boolean(true),
+            Function::Xor => self.fn_xor(args, cell),
+            // Math and trigonometry
+            Function::Sin => self.fn_sin(args, cell),
+            Function::Cos => self.fn_cos(args, cell),
+            Function::Tan => self.fn_tan(args, cell),
+
+            Function::Asin => self.fn_asin(args, cell),
+            Function::Acos => self.fn_acos(args, cell),
+            Function::Atan => self.fn_atan(args, cell),
+
+            Function::Sinh => self.fn_sinh(args, cell),
+            Function::Cosh => self.fn_cosh(args, cell),
+            Function::Tanh => self.fn_tanh(args, cell),
+
+            Function::Asinh => self.fn_asinh(args, cell),
+            Function::Acosh => self.fn_acosh(args, cell),
+            Function::Atanh => self.fn_atanh(args, cell),
+
+            Function::Pi => self.fn_pi(args, cell),
+            Function::Abs => self.fn_abs(args, cell),
+
+            Function::Sqrt => self.fn_sqrt(args, cell),
+            Function::Sqrtpi => self.fn_sqrtpi(args, cell),
+            Function::Atan2 => self.fn_atan2(args, cell),
+            Function::Power => self.fn_power(args, cell),
+
+            Function::Max => self.fn_max(args, cell),
+            Function::Min => self.fn_min(args, cell),
+            Function::Product => self.fn_product(args, cell),
+            Function::Rand => self.fn_rand(args, cell),
+            Function::Randbetween => self.fn_randbetween(args, cell),
+            Function::Round => self.fn_round(args, cell),
+            Function::Rounddown => self.fn_rounddown(args, cell),
+            Function::Roundup => self.fn_roundup(args, cell),
+            Function::Sum => self.fn_sum(args, cell),
+            Function::Sumif => self.fn_sumif(args, cell),
+            Function::Sumifs => self.fn_sumifs(args, cell),
+
+            // Lookup and Reference
+            Function::Choose => self.fn_choose(args, cell),
+            Function::Column => self.fn_column(args, cell),
+            Function::Columns => self.fn_columns(args, cell),
+            Function::Index => self.fn_index(args, cell),
+            Function::Indirect => self.fn_indirect(args, cell),
+            Function::Hlookup => self.fn_hlookup(args, cell),
+            Function::Lookup => self.fn_lookup(args, cell),
+            Function::Match => self.fn_match(args, cell),
+            Function::Offset => self.fn_offset(args, cell),
+            Function::Row => self.fn_row(args, cell),
+            Function::Rows => self.fn_rows(args, cell),
+            Function::Vlookup => self.fn_vlookup(args, cell),
+            Function::Xlookup => self.fn_xlookup(args, cell),
+            // Text
+            Function::Concatenate => self.fn_concatenate(args, cell),
+            Function::Exact => self.fn_exact(args, cell),
+            Function::Value => self.fn_value(args, cell),
+            Function::T => self.fn_t(args, cell),
+            Function::Valuetotext => self.fn_valuetotext(args, cell),
+            Function::Concat => self.fn_concat(args, cell),
+            Function::Find => self.fn_find(args, cell),
+            Function::Left => self.fn_left(args, cell),
+            Function::Len => self.fn_len(args, cell),
+            Function::Lower => self.fn_lower(args, cell),
+            Function::Mid => self.fn_mid(args, cell),
+            Function::Right => self.fn_right(args, cell),
+            Function::Search => self.fn_search(args, cell),
+            Function::Text => self.fn_text(args, cell),
+            Function::Trim => self.fn_trim(args, cell),
+            Function::Upper => self.fn_upper(args, cell),
+            // Information
+            Function::Isnumber => self.fn_isnumber(args, cell),
+            Function::Isnontext => self.fn_isnontext(args, cell),
+            Function::Istext => self.fn_istext(args, cell),
+            Function::Islogical => self.fn_islogical(args, cell),
+            Function::Isblank => self.fn_isblank(args, cell),
+            Function::Iserr => self.fn_iserr(args, cell),
+            Function::Iserror => self.fn_iserror(args, cell),
+            Function::Isna => self.fn_isna(args, cell),
+            Function::Na => CalcResult::new_error(Error::NA, cell, "".to_string()),
+            Function::Isref => self.fn_isref(args, cell),
+            Function::Isodd => self.fn_isodd(args, cell),
+            Function::Iseven => self.fn_iseven(args, cell),
+            Function::ErrorType => self.fn_errortype(args, cell),
+            Function::Isformula => self.fn_isformula(args, cell),
+            Function::Type => self.fn_type(args, cell),
+            Function::Sheet => self.fn_sheet(args, cell),
+            // Statistical
+            Function::Average => self.fn_average(args, cell),
+            Function::Averagea => self.fn_averagea(args, cell),
+            Function::Averageif => self.fn_averageif(args, cell),
+            Function::Averageifs => self.fn_averageifs(args, cell),
+            Function::Count => self.fn_count(args, cell),
+            Function::Counta => self.fn_counta(args, cell),
+            Function::Countblank => self.fn_countblank(args, cell),
+            Function::Countif => self.fn_countif(args, cell),
+            Function::Countifs => self.fn_countifs(args, cell),
+            Function::Maxifs => self.fn_maxifs(args, cell),
+            Function::Minifs => self.fn_minifs(args, cell),
+            // Date and Time
+            Function::Year => self.fn_year(args, cell),
+            Function::Day => self.fn_day(args, cell),
+            Function::Eomonth => self.fn_eomonth(args, cell),
+            Function::Month => self.fn_month(args, cell),
+            Function::Date => self.fn_date(args, cell),
+            Function::Edate => self.fn_edate(args, cell),
+            Function::Today => self.fn_today(args, cell),
+            Function::Now => self.fn_now(args, cell),
+            // Financial
+            Function::Pmt => self.fn_pmt(args, cell),
+            Function::Pv => self.fn_pv(args, cell),
+            Function::Rate => self.fn_rate(args, cell),
+            Function::Nper => self.fn_nper(args, cell),
+            Function::Fv => self.fn_fv(args, cell),
+            Function::Ppmt => self.fn_ppmt(args, cell),
+            Function::Ipmt => self.fn_ipmt(args, cell),
+            Function::Npv => self.fn_npv(args, cell),
+            Function::Mirr => self.fn_mirr(args, cell),
+            Function::Irr => self.fn_irr(args, cell),
+            Function::Xirr => self.fn_xirr(args, cell),
+            Function::Xnpv => self.fn_xnpv(args, cell),
+            Function::Rept => self.fn_rept(args, cell),
+            Function::Textafter => self.fn_textafter(args, cell),
+            Function::Textbefore => self.fn_textbefore(args, cell),
+            Function::Textjoin => self.fn_textjoin(args, cell),
+            Function::Substitute => self.fn_substitute(args, cell),
+            Function::Ispmt => self.fn_ispmt(args, cell),
+            Function::Rri => self.fn_rri(args, cell),
+            Function::Sln => self.fn_sln(args, cell),
+            Function::Syd => self.fn_syd(args, cell),
+            Function::Nominal => self.fn_nominal(args, cell),
+            Function::Effect => self.fn_effect(args, cell),
+            Function::Pduration => self.fn_pduration(args, cell),
+            Function::Tbillyield => self.fn_tbillyield(args, cell),
+            Function::Tbillprice => self.fn_tbillprice(args, cell),
+            Function::Tbilleq => self.fn_tbilleq(args, cell),
+            Function::Dollarde => self.fn_dollarde(args, cell),
+            Function::Dollarfr => self.fn_dollarfr(args, cell),
+            Function::Ddb => self.fn_ddb(args, cell),
+            Function::Db => self.fn_db(args, cell),
+            Function::Cumprinc => self.fn_cumprinc(args, cell),
+            Function::Cumipmt => self.fn_cumipmt(args, cell),
+            // Engineering
+            Function::Besseli => self.fn_besseli(args, cell),
+            Function::Besselj => self.fn_besselj(args, cell),
+            Function::Besselk => self.fn_besselk(args, cell),
+            Function::Bessely => self.fn_bessely(args, cell),
+            Function::Erf => self.fn_erf(args, cell),
+            Function::ErfPrecise => self.fn_erfprecise(args, cell),
+            Function::Erfc => self.fn_erfc(args, cell),
+            Function::ErfcPrecise => self.fn_erfcprecise(args, cell),
+            Function::Bin2dec => self.fn_bin2dec(args, cell),
+            Function::Bin2hex => self.fn_bin2hex(args, cell),
+            Function::Bin2oct => self.fn_bin2oct(args, cell),
+            Function::Dec2Bin => self.fn_dec2bin(args, cell),
+            Function::Dec2hex => self.fn_dec2hex(args, cell),
+            Function::Dec2oct => self.fn_dec2oct(args, cell),
+            Function::Hex2bin => self.fn_hex2bin(args, cell),
+            Function::Hex2dec => self.fn_hex2dec(args, cell),
+            Function::Hex2oct => self.fn_hex2oct(args, cell),
+            Function::Oct2bin => self.fn_oct2bin(args, cell),
+            Function::Oct2dec => self.fn_oct2dec(args, cell),
+            Function::Oct2hex => self.fn_oct2hex(args, cell),
+            Function::Bitand => self.fn_bitand(args, cell),
+            Function::Bitlshift => self.fn_bitlshift(args, cell),
+            Function::Bitor => self.fn_bitor(args, cell),
+            Function::Bitrshift => self.fn_bitrshift(args, cell),
+            Function::Bitxor => self.fn_bitxor(args, cell),
+            Function::Complex => self.fn_complex(args, cell),
+            Function::Imabs => self.fn_imabs(args, cell),
+            Function::Imaginary => self.fn_imaginary(args, cell),
+            Function::Imargument => self.fn_imargument(args, cell),
+            Function::Imconjugate => self.fn_imconjugate(args, cell),
+            Function::Imcos => self.fn_imcos(args, cell),
+            Function::Imcosh => self.fn_imcosh(args, cell),
+            Function::Imcot => self.fn_imcot(args, cell),
+            Function::Imcsc => self.fn_imcsc(args, cell),
+            Function::Imcsch => self.fn_imcsch(args, cell),
+            Function::Imdiv => self.fn_imdiv(args, cell),
+            Function::Imexp => self.fn_imexp(args, cell),
+            Function::Imln => self.fn_imln(args, cell),
+            Function::Imlog10 => self.fn_imlog10(args, cell),
+            Function::Imlog2 => self.fn_imlog2(args, cell),
+            Function::Impower => self.fn_impower(args, cell),
+            Function::Improduct => self.fn_improduct(args, cell),
+            Function::Imreal => self.fn_imreal(args, cell),
+            Function::Imsec => self.fn_imsec(args, cell),
+            Function::Imsech => self.fn_imsech(args, cell),
+            Function::Imsin => self.fn_imsin(args, cell),
+            Function::Imsinh => self.fn_imsinh(args, cell),
+            Function::Imsqrt => self.fn_imsqrt(args, cell),
+            Function::Imsub => self.fn_imsub(args, cell),
+            Function::Imsum => self.fn_imsum(args, cell),
+            Function::Imtan => self.fn_imtan(args, cell),
+            Function::Convert => self.fn_convert(args, cell),
+            Function::Delta => self.fn_delta(args, cell),
+            Function::Gestep => self.fn_gestep(args, cell),
+
+            Function::Subtotal => self.fn_subtotal(args, cell),
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/statistical.rs.html b/src/ironcalc_base/functions/statistical.rs.html new file mode 100644 index 0000000..3faa6bd --- /dev/null +++ b/src/ironcalc_base/functions/statistical.rs.html @@ -0,0 +1,1249 @@ +statistical.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+
use crate::constants::{LAST_COLUMN, LAST_ROW};
+use crate::{
+    calc_result::{CalcResult, CellReference, Range},
+    expressions::parser::Node,
+    expressions::token::Error,
+    model::Model,
+};
+
+use super::util::build_criteria;
+
+impl Model {
+    pub(crate) fn fn_average(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.is_empty() {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let mut count = 0.0;
+        let mut sum = 0.0;
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::Number(value) => {
+                    count += 1.0;
+                    sum += value;
+                }
+                CalcResult::Boolean(b) => {
+                    if let Node::ReferenceKind { .. } = arg {
+                    } else {
+                        sum += if b { 1.0 } else { 0.0 };
+                        count += 1.0;
+                    }
+                }
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    for row in left.row..(right.row + 1) {
+                        for column in left.column..(right.column + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::Number(value) => {
+                                    count += 1.0;
+                                    sum += value;
+                                }
+                                error @ CalcResult::Error { .. } => return error,
+                                CalcResult::Range { .. } => {
+                                    return CalcResult::new_error(
+                                        Error::ERROR,
+                                        cell,
+                                        "Unexpected Range".to_string(),
+                                    );
+                                }
+                                _ => {}
+                            }
+                        }
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::String(s) => {
+                    if let Node::ReferenceKind { .. } = arg {
+                        // Do nothing
+                    } else if let Ok(t) = s.parse::<f64>() {
+                        sum += t;
+                        count += 1.0;
+                    } else {
+                        return CalcResult::Error {
+                            error: Error::VALUE,
+                            origin: cell,
+                            message: "Argument cannot be cast into number".to_string(),
+                        };
+                    }
+                }
+                _ => {
+                    // Ignore everything else
+                }
+            };
+        }
+        if count == 0.0 {
+            return CalcResult::Error {
+                error: Error::DIV,
+                origin: cell,
+                message: "Division by Zero".to_string(),
+            };
+        }
+        CalcResult::Number(sum / count)
+    }
+
+    pub(crate) fn fn_averagea(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.is_empty() {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let mut count = 0.0;
+        let mut sum = 0.0;
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    for row in left.row..(right.row + 1) {
+                        for column in left.column..(right.column + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::String(_) => count += 1.0,
+                                CalcResult::Number(value) => {
+                                    count += 1.0;
+                                    sum += value;
+                                }
+                                CalcResult::Boolean(b) => {
+                                    if b {
+                                        sum += 1.0;
+                                    }
+                                    count += 1.0;
+                                }
+                                error @ CalcResult::Error { .. } => return error,
+                                CalcResult::Range { .. } => {
+                                    return CalcResult::new_error(
+                                        Error::ERROR,
+                                        cell,
+                                        "Unexpected Range".to_string(),
+                                    );
+                                }
+                                CalcResult::EmptyCell | CalcResult::EmptyArg => {}
+                            }
+                        }
+                    }
+                }
+                CalcResult::Number(value) => {
+                    count += 1.0;
+                    sum += value;
+                }
+                CalcResult::String(s) => {
+                    if let Node::ReferenceKind { .. } = arg {
+                        // Do nothing
+                        count += 1.0;
+                    } else if let Ok(t) = s.parse::<f64>() {
+                        sum += t;
+                        count += 1.0;
+                    } else {
+                        return CalcResult::Error {
+                            error: Error::VALUE,
+                            origin: cell,
+                            message: "Argument cannot be cast into number".to_string(),
+                        };
+                    }
+                }
+                CalcResult::Boolean(b) => {
+                    count += 1.0;
+                    if b {
+                        sum += 1.0;
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::EmptyCell | CalcResult::EmptyArg => {}
+            };
+        }
+        if count == 0.0 {
+            return CalcResult::Error {
+                error: Error::DIV,
+                origin: cell,
+                message: "Division by Zero".to_string(),
+            };
+        }
+        CalcResult::Number(sum / count)
+    }
+
+    pub(crate) fn fn_count(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.is_empty() {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let mut result = 0.0;
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::Number(_) => {
+                    result += 1.0;
+                }
+                CalcResult::Boolean(_) => {
+                    if !matches!(arg, Node::ReferenceKind { .. }) {
+                        result += 1.0;
+                    }
+                }
+                CalcResult::String(s) => {
+                    if !matches!(arg, Node::ReferenceKind { .. }) && s.parse::<f64>().is_ok() {
+                        result += 1.0;
+                    }
+                }
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    for row in left.row..(right.row + 1) {
+                        for column in left.column..(right.column + 1) {
+                            if let CalcResult::Number(_) = self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                result += 1.0;
+                            }
+                        }
+                    }
+                }
+                _ => {
+                    // Ignore everything else
+                }
+            };
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_counta(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.is_empty() {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let mut result = 0.0;
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::EmptyCell | CalcResult::EmptyArg => {}
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    for row in left.row..(right.row + 1) {
+                        for column in left.column..(right.column + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::EmptyCell | CalcResult::EmptyArg => {}
+                                _ => {
+                                    result += 1.0;
+                                }
+                            }
+                        }
+                    }
+                }
+                _ => {
+                    result += 1.0;
+                }
+            };
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_countblank(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        // COUNTBLANK requires only one argument
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let mut result = 0.0;
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::EmptyCell | CalcResult::EmptyArg => result += 1.0,
+                CalcResult::String(s) => {
+                    if s.is_empty() {
+                        result += 1.0
+                    }
+                }
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    for row in left.row..(right.row + 1) {
+                        for column in left.column..(right.column + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::EmptyCell | CalcResult::EmptyArg => result += 1.0,
+                                CalcResult::String(s) => {
+                                    if s.is_empty() {
+                                        result += 1.0
+                                    }
+                                }
+                                _ => {}
+                            }
+                        }
+                    }
+                }
+                _ => {}
+            };
+        }
+        CalcResult::Number(result)
+    }
+
+    pub(crate) fn fn_countif(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 2 {
+            let arguments = vec![args[0].clone(), args[1].clone()];
+            self.fn_countifs(&arguments, cell)
+        } else {
+            CalcResult::new_args_number_error(cell)
+        }
+    }
+
+    /// AVERAGEIF(criteria_range, criteria, [average_range])
+    /// if average_rage is missing then criteria_range will be used
+    pub(crate) fn fn_averageif(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 2 {
+            let arguments = vec![args[0].clone(), args[0].clone(), args[1].clone()];
+            self.fn_averageifs(&arguments, cell)
+        } else if args.len() == 3 {
+            let arguments = vec![args[2].clone(), args[0].clone(), args[1].clone()];
+            self.fn_averageifs(&arguments, cell)
+        } else {
+            CalcResult::new_args_number_error(cell)
+        }
+    }
+
+    // FIXME: This function shares a lot of code with apply_ifs. Can we merge them?
+    pub(crate) fn fn_countifs(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let args_count = args.len();
+        if args_count < 2 || args_count % 2 == 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+
+        let case_count = args_count / 2;
+        // NB: this is a beautiful example of the borrow checker
+        // The order of these two definitions cannot be swapped.
+        let mut criteria = Vec::new();
+        let mut fn_criteria = Vec::new();
+        let ranges = &mut Vec::new();
+        for case_index in 0..case_count {
+            let criterion = self.evaluate_node_in_context(&args[case_index * 2 + 1], cell);
+            criteria.push(criterion);
+            // NB: We cannot do:
+            // fn_criteria.push(build_criteria(&criterion));
+            // because criterion doesn't live long enough
+            let result = self.evaluate_node_in_context(&args[case_index * 2], cell);
+            if result.is_error() {
+                return result;
+            }
+            if let CalcResult::Range { left, right } = result {
+                if left.sheet != right.sheet {
+                    return CalcResult::new_error(
+                        Error::VALUE,
+                        cell,
+                        "Ranges are in different sheets".to_string(),
+                    );
+                }
+                // TODO test ranges are of the same size as sum_range
+                ranges.push(Range { left, right });
+            } else {
+                return CalcResult::new_error(Error::VALUE, cell, "Expected a range".to_string());
+            }
+        }
+        for criterion in criteria.iter() {
+            fn_criteria.push(build_criteria(criterion));
+        }
+
+        let mut total = 0.0;
+        let first_range = &ranges[0];
+        let left_row = first_range.left.row;
+        let left_column = first_range.left.column;
+        let right_row = first_range.right.row;
+        let right_column = first_range.right.column;
+
+        let dimension = self
+            .workbook
+            .worksheet(first_range.left.sheet)
+            .expect("Sheet expected during evaluation.")
+            .dimension();
+        let max_row = dimension.max_row;
+        let max_column = dimension.max_column;
+
+        let open_row = left_row == 1 && right_row == LAST_ROW;
+        let open_column = left_column == 1 && right_column == LAST_COLUMN;
+
+        for row in left_row..right_row + 1 {
+            if open_row && row > max_row {
+                // If the row is larger than the max row in the sheet then all cells are empty.
+                // We compute it only once
+                let mut is_true = true;
+                for fn_criterion in fn_criteria.iter() {
+                    if !fn_criterion(&CalcResult::EmptyCell) {
+                        is_true = false;
+                        break;
+                    }
+                }
+                if is_true {
+                    total += ((LAST_ROW - max_row) * (right_column - left_column + 1)) as f64;
+                }
+                break;
+            }
+            for column in left_column..right_column + 1 {
+                if open_column && column > max_column {
+                    // If the column is larger than the max column in the sheet then all cells are empty.
+                    // We compute it only once
+                    let mut is_true = true;
+                    for fn_criterion in fn_criteria.iter() {
+                        if !fn_criterion(&CalcResult::EmptyCell) {
+                            is_true = false;
+                            break;
+                        }
+                    }
+                    if is_true {
+                        total += (LAST_COLUMN - max_column) as f64;
+                    }
+                    break;
+                }
+                let mut is_true = true;
+                for case_index in 0..case_count {
+                    // We check if value in range n meets criterion n
+                    let range = &ranges[case_index];
+                    let fn_criterion = &fn_criteria[case_index];
+                    let value = self.evaluate_cell(CellReference {
+                        sheet: range.left.sheet,
+                        row: range.left.row + row - first_range.left.row,
+                        column: range.left.column + column - first_range.left.column,
+                    });
+                    if !fn_criterion(&value) {
+                        is_true = false;
+                        break;
+                    }
+                }
+                if is_true {
+                    total += 1.0;
+                }
+            }
+        }
+        CalcResult::Number(total)
+    }
+
+    pub(crate) fn apply_ifs<F>(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mut apply: F,
+    ) -> Result<(), CalcResult>
+    where
+        F: FnMut(f64),
+    {
+        let args_count = args.len();
+        if args_count < 3 || args_count % 2 == 0 {
+            return Err(CalcResult::new_args_number_error(cell));
+        }
+        let arg_0 = self.evaluate_node_in_context(&args[0], cell);
+        if arg_0.is_error() {
+            return Err(arg_0);
+        }
+        let sum_range = if let CalcResult::Range { left, right } = arg_0 {
+            if left.sheet != right.sheet {
+                return Err(CalcResult::new_error(
+                    Error::VALUE,
+                    cell,
+                    "Ranges are in different sheets".to_string(),
+                ));
+            }
+            Range { left, right }
+        } else {
+            return Err(CalcResult::new_error(
+                Error::VALUE,
+                cell,
+                "Expected a range".to_string(),
+            ));
+        };
+
+        let case_count = (args_count - 1) / 2;
+        // NB: this is a beautiful example of the borrow checker
+        // The order of these two definitions cannot be swapped.
+        let mut criteria = Vec::new();
+        let mut fn_criteria = Vec::new();
+        let ranges = &mut Vec::new();
+        for case_index in 1..=case_count {
+            let criterion = self.evaluate_node_in_context(&args[case_index * 2], cell);
+            // NB: criterion might be an error. That's ok
+            criteria.push(criterion);
+            // NB: We cannot do:
+            // fn_criteria.push(build_criteria(&criterion));
+            // because criterion doesn't live long enough
+            let result = self.evaluate_node_in_context(&args[case_index * 2 - 1], cell);
+            if result.is_error() {
+                return Err(result);
+            }
+            if let CalcResult::Range { left, right } = result {
+                if left.sheet != right.sheet {
+                    return Err(CalcResult::new_error(
+                        Error::VALUE,
+                        cell,
+                        "Ranges are in different sheets".to_string(),
+                    ));
+                }
+                // TODO test ranges are of the same size as sum_range
+                ranges.push(Range { left, right });
+            } else {
+                return Err(CalcResult::new_error(
+                    Error::VALUE,
+                    cell,
+                    "Expected a range".to_string(),
+                ));
+            }
+        }
+        for criterion in criteria.iter() {
+            fn_criteria.push(build_criteria(criterion));
+        }
+
+        let left_row = sum_range.left.row;
+        let left_column = sum_range.left.column;
+        let mut right_row = sum_range.right.row;
+        let mut right_column = sum_range.right.column;
+
+        if left_row == 1 && right_row == LAST_ROW {
+            right_row = self
+                .workbook
+                .worksheet(sum_range.left.sheet)
+                .expect("Sheet expected during evaluation.")
+                .dimension()
+                .max_row;
+        }
+        if left_column == 1 && right_column == LAST_COLUMN {
+            right_column = self
+                .workbook
+                .worksheet(sum_range.left.sheet)
+                .expect("Sheet expected during evaluation.")
+                .dimension()
+                .max_column;
+        }
+
+        for row in left_row..right_row + 1 {
+            for column in left_column..right_column + 1 {
+                let mut is_true = true;
+                for case_index in 0..case_count {
+                    // We check if value in range n meets criterion n
+                    let range = &ranges[case_index];
+                    let fn_criterion = &fn_criteria[case_index];
+                    let value = self.evaluate_cell(CellReference {
+                        sheet: range.left.sheet,
+                        row: range.left.row + row - sum_range.left.row,
+                        column: range.left.column + column - sum_range.left.column,
+                    });
+                    if !fn_criterion(&value) {
+                        is_true = false;
+                        break;
+                    }
+                }
+                if is_true {
+                    let v = self.evaluate_cell(CellReference {
+                        sheet: sum_range.left.sheet,
+                        row,
+                        column,
+                    });
+                    match v {
+                        CalcResult::Number(n) => apply(n),
+                        CalcResult::Error { .. } => return Err(v),
+                        _ => {}
+                    }
+                }
+            }
+        }
+        Ok(())
+    }
+
+    pub(crate) fn fn_averageifs(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let mut total = 0.0;
+        let mut count = 0.0;
+
+        let average = |value: f64| {
+            total += value;
+            count += 1.0;
+        };
+        if let Err(e) = self.apply_ifs(args, cell, average) {
+            return e;
+        }
+
+        if count == 0.0 {
+            return CalcResult::Error {
+                error: Error::DIV,
+                origin: cell,
+                message: "division by 0".to_string(),
+            };
+        }
+        CalcResult::Number(total / count)
+    }
+
+    pub(crate) fn fn_minifs(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let mut min = f64::INFINITY;
+        let apply_min = |value: f64| min = value.min(min);
+        if let Err(e) = self.apply_ifs(args, cell, apply_min) {
+            return e;
+        }
+
+        if min.is_infinite() {
+            min = 0.0;
+        }
+        CalcResult::Number(min)
+    }
+
+    pub(crate) fn fn_maxifs(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let mut max = -f64::INFINITY;
+        let apply_max = |value: f64| max = value.max(max);
+        if let Err(e) = self.apply_ifs(args, cell, apply_max) {
+            return e;
+        }
+        if max.is_infinite() {
+            max = 0.0;
+        }
+        CalcResult::Number(max)
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/subtotal.rs.html b/src/ironcalc_base/functions/subtotal.rs.html new file mode 100644 index 0000000..56ac8a0 --- /dev/null +++ b/src/ironcalc_base/functions/subtotal.rs.html @@ -0,0 +1,1169 @@ +subtotal.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+
use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::{
+        parser::{parse_range, Node},
+        token::Error,
+    },
+    functions::Function,
+    model::Model,
+};
+
+/// Excel has a complicated way of filtering + hidden rows
+/// As a first a approximation a table can either have filtered rows or hidden rows, but not both.
+/// Internally hey both will be marked as hidden rows. Hidden rows
+/// The behaviour is the same for SUBTOTAL(100s,) => ignore those
+/// But changes for the SUBTOTAL(1-11, ) those ignore filtered but take hidden into account.
+/// In Excel filters are non-dynamic. Once you apply filters in a table (say value in column 1 should > 20) they
+/// stay that way, even if you change the value of the values in the table after the fact.
+/// If you try to hide rows in a table with filtered rows they will behave as if filtered
+/// // Also subtotals ignore subtotals
+///
+#[derive(PartialEq)]
+enum SubTotalMode {
+    Full,
+    SkipHidden,
+}
+
+#[derive(PartialEq, Debug)]
+pub enum CellTableStatus {
+    Normal,
+    Hidden,
+    Filtered,
+}
+
+impl Model {
+    fn get_table_for_cell(&self, sheet_index: u32, row: i32, column: i32) -> bool {
+        let worksheet = match self.workbook.worksheet(sheet_index) {
+            Ok(ws) => ws,
+            Err(_) => return false,
+        };
+        for table in self.workbook.tables.values() {
+            if worksheet.name != table.sheet_name {
+                continue;
+            }
+            // (column, row, column, row)
+            if let Ok((column1, row1, column2, row2)) = parse_range(&table.reference) {
+                if ((column >= column1) && (column <= column2)) && ((row >= row1) && (row <= row2))
+                {
+                    return table.has_filters;
+                }
+            }
+        }
+        false
+    }
+
+    fn cell_hidden_status(&self, sheet_index: u32, row: i32, column: i32) -> CellTableStatus {
+        let worksheet = self.workbook.worksheet(sheet_index).expect("");
+        let mut hidden = false;
+        for row_style in &worksheet.rows {
+            if row_style.r == row {
+                hidden = row_style.hidden;
+                break;
+            }
+        }
+        if !hidden {
+            return CellTableStatus::Normal;
+        }
+        // The row is hidden we need to know if the table has filters
+        if self.get_table_for_cell(sheet_index, row, column) {
+            CellTableStatus::Filtered
+        } else {
+            CellTableStatus::Hidden
+        }
+    }
+
+    // FIXME(TD): This is too much
+    fn cell_is_subtotal(&self, sheet_index: u32, row: i32, column: i32) -> bool {
+        let row_data = match self.workbook.worksheets[sheet_index as usize]
+            .sheet_data
+            .get(&row)
+        {
+            Some(r) => r,
+            None => return false,
+        };
+        let cell = match row_data.get(&column) {
+            Some(c) => c,
+            None => {
+                return false;
+            }
+        };
+
+        match cell.get_formula() {
+            Some(f) => {
+                let node = &self.parsed_formulas[sheet_index as usize][f as usize];
+                matches!(
+                    node,
+                    Node::FunctionKind {
+                        kind: Function::Subtotal,
+                        args: _
+                    }
+                )
+            }
+            None => false,
+        }
+    }
+
+    fn subtotal_get_values(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mode: SubTotalMode,
+    ) -> Result<Vec<f64>, CalcResult> {
+        let mut result: Vec<f64> = Vec::new();
+        for arg in args {
+            match arg {
+                Node::FunctionKind {
+                    kind: Function::Subtotal,
+                    args: _,
+                } => {
+                    // skip
+                }
+                _ => {
+                    match self.evaluate_node_with_reference(arg, cell) {
+                        CalcResult::String(_) | CalcResult::Boolean(_) => {
+                            // Skip
+                        }
+                        CalcResult::Number(f) => result.push(f),
+                        error @ CalcResult::Error { .. } => {
+                            return Err(error);
+                        }
+                        CalcResult::Range { left, right } => {
+                            if left.sheet != right.sheet {
+                                return Err(CalcResult::new_error(
+                                    Error::VALUE,
+                                    cell,
+                                    "Ranges are in different sheets".to_string(),
+                                ));
+                            }
+                            // We are not expecting subtotal to have open ranges
+                            let row1 = left.row;
+                            let row2 = right.row;
+                            let column1 = left.column;
+                            let column2 = right.column;
+
+                            for row in row1..=row2 {
+                                let cell_status = self.cell_hidden_status(left.sheet, row, column1);
+                                if cell_status == CellTableStatus::Filtered {
+                                    continue;
+                                }
+                                if mode == SubTotalMode::SkipHidden
+                                    && cell_status == CellTableStatus::Hidden
+                                {
+                                    continue;
+                                }
+                                for column in column1..=column2 {
+                                    if self.cell_is_subtotal(left.sheet, row, column) {
+                                        continue;
+                                    }
+                                    match self.evaluate_cell(CellReference {
+                                        sheet: left.sheet,
+                                        row,
+                                        column,
+                                    }) {
+                                        CalcResult::Number(value) => {
+                                            result.push(value);
+                                        }
+                                        error @ CalcResult::Error { .. } => return Err(error),
+                                        _ => {
+                                            // We ignore booleans and strings
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        CalcResult::EmptyCell | CalcResult::EmptyArg => result.push(0.0),
+                    }
+                }
+            }
+        }
+        Ok(result)
+    }
+
+    pub(crate) fn fn_subtotal(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() < 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let value = match self.get_number(&args[0], cell) {
+            Ok(f) => f.trunc() as i32,
+            Err(s) => return s,
+        };
+        match value {
+            1 => self.subtotal_average(&args[1..], cell, SubTotalMode::Full),
+            2 => self.subtotal_count(&args[1..], cell, SubTotalMode::Full),
+            3 => self.subtotal_counta(&args[1..], cell, SubTotalMode::Full),
+            4 => self.subtotal_max(&args[1..], cell, SubTotalMode::Full),
+            5 => self.subtotal_min(&args[1..], cell, SubTotalMode::Full),
+            6 => self.subtotal_product(&args[1..], cell, SubTotalMode::Full),
+            7 => self.subtotal_stdevs(&args[1..], cell, SubTotalMode::Full),
+            8 => self.subtotal_stdevp(&args[1..], cell, SubTotalMode::Full),
+            9 => self.subtotal_sum(&args[1..], cell, SubTotalMode::Full),
+            10 => self.subtotal_vars(&args[1..], cell, SubTotalMode::Full),
+            11 => self.subtotal_varp(&args[1..], cell, SubTotalMode::Full),
+            101 => self.subtotal_average(&args[1..], cell, SubTotalMode::SkipHidden),
+            102 => self.subtotal_count(&args[1..], cell, SubTotalMode::SkipHidden),
+            103 => self.subtotal_counta(&args[1..], cell, SubTotalMode::SkipHidden),
+            104 => self.subtotal_max(&args[1..], cell, SubTotalMode::SkipHidden),
+            105 => self.subtotal_min(&args[1..], cell, SubTotalMode::SkipHidden),
+            106 => self.subtotal_product(&args[1..], cell, SubTotalMode::SkipHidden),
+            107 => self.subtotal_stdevs(&args[1..], cell, SubTotalMode::SkipHidden),
+            108 => self.subtotal_stdevp(&args[1..], cell, SubTotalMode::SkipHidden),
+            109 => self.subtotal_sum(&args[1..], cell, SubTotalMode::SkipHidden),
+            110 => self.subtotal_vars(&args[1..], cell, SubTotalMode::Full),
+            111 => self.subtotal_varp(&args[1..], cell, SubTotalMode::Full),
+            _ => CalcResult::new_error(
+                Error::VALUE,
+                cell,
+                format!("Invalid value for SUBTOTAL: {value}"),
+            ),
+        }
+    }
+
+    fn subtotal_vars(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mode: SubTotalMode,
+    ) -> CalcResult {
+        let values = match self.subtotal_get_values(args, cell, mode) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let mut result = 0.0;
+        let l = values.len();
+        for value in &values {
+            result += value;
+        }
+        if l < 2 {
+            return CalcResult::Error {
+                error: Error::DIV,
+                origin: cell,
+                message: "Division by 0!".to_string(),
+            };
+        }
+        // average
+        let average = result / (l as f64);
+        let mut result = 0.0;
+        for value in &values {
+            result += (value - average).powi(2) / (l as f64 - 1.0)
+        }
+
+        CalcResult::Number(result)
+    }
+
+    fn subtotal_varp(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mode: SubTotalMode,
+    ) -> CalcResult {
+        let values = match self.subtotal_get_values(args, cell, mode) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let mut result = 0.0;
+        let l = values.len();
+        for value in &values {
+            result += value;
+        }
+        if l == 0 {
+            return CalcResult::Error {
+                error: Error::DIV,
+                origin: cell,
+                message: "Division by 0!".to_string(),
+            };
+        }
+        // average
+        let average = result / (l as f64);
+        let mut result = 0.0;
+        for value in &values {
+            result += (value - average).powi(2) / (l as f64)
+        }
+        CalcResult::Number(result)
+    }
+
+    fn subtotal_stdevs(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mode: SubTotalMode,
+    ) -> CalcResult {
+        let values = match self.subtotal_get_values(args, cell, mode) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let mut result = 0.0;
+        let l = values.len();
+        for value in &values {
+            result += value;
+        }
+        if l < 2 {
+            return CalcResult::Error {
+                error: Error::DIV,
+                origin: cell,
+                message: "Division by 0!".to_string(),
+            };
+        }
+        // average
+        let average = result / (l as f64);
+        let mut result = 0.0;
+        for value in &values {
+            result += (value - average).powi(2) / (l as f64 - 1.0)
+        }
+
+        CalcResult::Number(result.sqrt())
+    }
+
+    fn subtotal_stdevp(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mode: SubTotalMode,
+    ) -> CalcResult {
+        let values = match self.subtotal_get_values(args, cell, mode) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let mut result = 0.0;
+        let l = values.len();
+        for value in &values {
+            result += value;
+        }
+        if l == 0 {
+            return CalcResult::Error {
+                error: Error::DIV,
+                origin: cell,
+                message: "Division by 0!".to_string(),
+            };
+        }
+        // average
+        let average = result / (l as f64);
+        let mut result = 0.0;
+        for value in &values {
+            result += (value - average).powi(2) / (l as f64)
+        }
+        CalcResult::Number(result.sqrt())
+    }
+
+    fn subtotal_counta(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mode: SubTotalMode,
+    ) -> CalcResult {
+        let mut counta = 0;
+        for arg in args {
+            match arg {
+                Node::FunctionKind {
+                    kind: Function::Subtotal,
+                    args: _,
+                } => {
+                    // skip
+                }
+                _ => {
+                    match self.evaluate_node_with_reference(arg, cell) {
+                        CalcResult::EmptyCell | CalcResult::EmptyArg => {
+                            // skip
+                        }
+                        CalcResult::Range { left, right } => {
+                            if left.sheet != right.sheet {
+                                return CalcResult::new_error(
+                                    Error::VALUE,
+                                    cell,
+                                    "Ranges are in different sheets".to_string(),
+                                );
+                            }
+                            // We are not expecting subtotal to have open ranges
+                            let row1 = left.row;
+                            let row2 = right.row;
+                            let column1 = left.column;
+                            let column2 = right.column;
+
+                            for row in row1..=row2 {
+                                let cell_status = self.cell_hidden_status(left.sheet, row, column1);
+                                if cell_status == CellTableStatus::Filtered {
+                                    continue;
+                                }
+                                if mode == SubTotalMode::SkipHidden
+                                    && cell_status == CellTableStatus::Hidden
+                                {
+                                    continue;
+                                }
+                                for column in column1..=column2 {
+                                    if self.cell_is_subtotal(left.sheet, row, column) {
+                                        continue;
+                                    }
+                                    match self.evaluate_cell(CellReference {
+                                        sheet: left.sheet,
+                                        row,
+                                        column,
+                                    }) {
+                                        CalcResult::EmptyCell | CalcResult::EmptyArg => {
+                                            // skip
+                                        }
+                                        _ => counta += 1,
+                                    }
+                                }
+                            }
+                        }
+                        CalcResult::String(_)
+                        | CalcResult::Number(_)
+                        | CalcResult::Boolean(_)
+                        | CalcResult::Error { .. } => counta += 1,
+                    }
+                }
+            }
+        }
+        CalcResult::Number(counta as f64)
+    }
+
+    fn subtotal_count(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mode: SubTotalMode,
+    ) -> CalcResult {
+        let mut count = 0;
+        for arg in args {
+            match arg {
+                Node::FunctionKind {
+                    kind: Function::Subtotal,
+                    args: _,
+                } => {
+                    // skip
+                }
+                _ => {
+                    match self.evaluate_node_with_reference(arg, cell) {
+                        CalcResult::Range { left, right } => {
+                            if left.sheet != right.sheet {
+                                return CalcResult::new_error(
+                                    Error::VALUE,
+                                    cell,
+                                    "Ranges are in different sheets".to_string(),
+                                );
+                            }
+                            // We are not expecting subtotal to have open ranges
+                            let row1 = left.row;
+                            let row2 = right.row;
+                            let column1 = left.column;
+                            let column2 = right.column;
+
+                            for row in row1..=row2 {
+                                let cell_status = self.cell_hidden_status(left.sheet, row, column1);
+                                if cell_status == CellTableStatus::Filtered {
+                                    continue;
+                                }
+                                if mode == SubTotalMode::SkipHidden
+                                    && cell_status == CellTableStatus::Hidden
+                                {
+                                    continue;
+                                }
+                                for column in column1..=column2 {
+                                    if self.cell_is_subtotal(left.sheet, row, column) {
+                                        continue;
+                                    }
+                                    if let CalcResult::Number(_) =
+                                        self.evaluate_cell(CellReference {
+                                            sheet: left.sheet,
+                                            row,
+                                            column,
+                                        })
+                                    {
+                                        count += 1;
+                                    }
+                                }
+                            }
+                        }
+                        // This hasn't been tested
+                        CalcResult::Number(_) => count += 1,
+                        _ => {}
+                    }
+                }
+            }
+        }
+        CalcResult::Number(count as f64)
+    }
+
+    fn subtotal_average(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mode: SubTotalMode,
+    ) -> CalcResult {
+        let values = match self.subtotal_get_values(args, cell, mode) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let mut result = 0.0;
+        let l = values.len();
+        for value in values {
+            result += value;
+        }
+        if l == 0 {
+            return CalcResult::Error {
+                error: Error::DIV,
+                origin: cell,
+                message: "Division by 0!".to_string(),
+            };
+        }
+        CalcResult::Number(result / (l as f64))
+    }
+
+    fn subtotal_sum(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mode: SubTotalMode,
+    ) -> CalcResult {
+        let values = match self.subtotal_get_values(args, cell, mode) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let mut result = 0.0;
+        for value in values {
+            result += value;
+        }
+        CalcResult::Number(result)
+    }
+
+    fn subtotal_product(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mode: SubTotalMode,
+    ) -> CalcResult {
+        let values = match self.subtotal_get_values(args, cell, mode) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let mut result = 1.0;
+        for value in values {
+            result *= value;
+        }
+        CalcResult::Number(result)
+    }
+
+    fn subtotal_max(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mode: SubTotalMode,
+    ) -> CalcResult {
+        let values = match self.subtotal_get_values(args, cell, mode) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let mut result = f64::NAN;
+        for value in values {
+            result = value.max(result);
+        }
+        if result.is_nan() {
+            return CalcResult::Number(0.0);
+        }
+        CalcResult::Number(result)
+    }
+
+    fn subtotal_min(
+        &mut self,
+        args: &[Node],
+        cell: CellReference,
+        mode: SubTotalMode,
+    ) -> CalcResult {
+        let values = match self.subtotal_get_values(args, cell, mode) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let mut result = f64::NAN;
+        for value in values {
+            result = value.min(result);
+        }
+        if result.is_nan() {
+            return CalcResult::Number(0.0);
+        }
+        CalcResult::Number(result)
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/text.rs.html b/src/ironcalc_base/functions/text.rs.html new file mode 100644 index 0000000..3667f8d --- /dev/null +++ b/src/ironcalc_base/functions/text.rs.html @@ -0,0 +1,2209 @@ +text.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+
use crate::{
+    calc_result::{CalcResult, CellReference},
+    constants::{LAST_COLUMN, LAST_ROW},
+    expressions::parser::Node,
+    expressions::token::Error,
+    formatter::format::{format_number, parse_formatted_number},
+    model::Model,
+    number_format::to_precision,
+};
+
+use super::{
+    text_util::{substitute, text_after, text_before, Case},
+    util::from_wildcard_to_regex,
+};
+
+/// Finds the first instance of 'search_for' in text starting at char index start
+fn find(search_for: &str, text: &str, start: usize) -> Option<i32> {
+    let ch = text.chars();
+    let mut byte_index = 0;
+    for (char_index, c) in ch.enumerate() {
+        if char_index + 1 >= start && text[byte_index..].starts_with(search_for) {
+            return Some((char_index + 1) as i32);
+        }
+        byte_index += c.len_utf8();
+    }
+    None
+}
+
+/// You can use the wildcard characters — the question mark (?) and asterisk (*) — in the find_text argument.
+/// * A question mark matches any single character.
+/// * An asterisk matches any sequence of characters.
+/// * If you want to find an actual question mark or asterisk, type a tilde (~) before the character.
+fn search(search_for: &str, text: &str, start: usize) -> Option<i32> {
+    let re = match from_wildcard_to_regex(search_for, false) {
+        Ok(r) => r,
+        Err(_) => return None,
+    };
+
+    let ch = text.chars();
+    let mut byte_index = 0;
+    for (char_index, c) in ch.enumerate() {
+        if char_index + 1 >= start {
+            if let Some(m) = re.find(&text[byte_index..]) {
+                return Some((text[0..(m.start() + byte_index)].chars().count() as i32) + 1);
+            } else {
+                return None;
+            }
+        }
+        byte_index += c.len_utf8();
+    }
+    None
+}
+
+impl Model {
+    pub(crate) fn fn_concat(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let mut result = "".to_string();
+        for arg in args {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::String(value) => result = format!("{}{}", result, value),
+                CalcResult::Number(value) => result = format!("{}{}", result, value),
+                CalcResult::EmptyCell | CalcResult::EmptyArg => {}
+                CalcResult::Boolean(value) => {
+                    if value {
+                        result = format!("{}TRUE", result);
+                    } else {
+                        result = format!("{}FALSE", result);
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    for row in left.row..(right.row + 1) {
+                        for column in left.column..(right.column + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::String(value) => {
+                                    result = format!("{}{}", result, value);
+                                }
+                                CalcResult::Number(value) => {
+                                    result = format!("{}{}", result, value)
+                                }
+                                CalcResult::Boolean(value) => {
+                                    if value {
+                                        result = format!("{}TRUE", result);
+                                    } else {
+                                        result = format!("{}FALSE", result);
+                                    }
+                                }
+                                error @ CalcResult::Error { .. } => return error,
+                                CalcResult::EmptyCell | CalcResult::EmptyArg => {}
+                                CalcResult::Range { .. } => {}
+                            }
+                        }
+                    }
+                }
+            };
+        }
+        CalcResult::String(result)
+    }
+    pub(crate) fn fn_text(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 2 {
+            let value = match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::Number(f) => f,
+                CalcResult::String(s) => {
+                    return CalcResult::String(s);
+                }
+                CalcResult::Boolean(b) => {
+                    return CalcResult::Boolean(b);
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::Range { .. } => {
+                    // Implicit Intersection not implemented
+                    return CalcResult::Error {
+                        error: Error::NIMPL,
+                        origin: cell,
+                        message: "Implicit Intersection not implemented".to_string(),
+                    };
+                }
+                CalcResult::EmptyCell | CalcResult::EmptyArg => 0.0,
+            };
+            let format_code = match self.get_string(&args[1], cell) {
+                Ok(s) => s,
+                Err(s) => return s,
+            };
+            let d = format_number(value, &format_code, &self.locale);
+            if let Some(_e) = d.error {
+                return CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "Invalid format code".to_string(),
+                };
+            }
+            CalcResult::String(d.text)
+        } else {
+            CalcResult::new_args_number_error(cell)
+        }
+    }
+
+    /// FIND(find_text, within_text, [start_num])
+    ///  * FIND and FINDB are case sensitive and don't allow wildcard characters.
+    ///  * If find_text is "" (empty text), FIND matches the first character in the search string (that is, the character numbered start_num or 1).
+    ///  * Find_text cannot contain any wildcard characters.
+    ///  * If find_text does not appear in within_text, FIND and FINDB return the #VALUE! error value.
+    ///  * If start_num is not greater than zero, FIND and FINDB return the #VALUE! error value.
+    ///  * If start_num is greater than the length of within_text, FIND and FINDB return the #VALUE! error value.
+    /// NB: FINDB is not implemented. It is the same as FIND function unless locale is a DBCS (Double Byte Character Set)
+    pub(crate) fn fn_find(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() < 2 || args.len() > 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let find_text = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let within_text = match self.get_string(&args[1], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let start_num = if args.len() == 3 {
+            match self.get_number(&args[2], cell) {
+                Ok(s) => s.floor(),
+                Err(s) => return s,
+            }
+        } else {
+            1.0
+        };
+
+        if start_num < 1.0 {
+            return CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "Start num must be >= 1".to_string(),
+            };
+        }
+        let start_num = start_num as usize;
+
+        if start_num > within_text.len() {
+            return CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "Start num greater than length".to_string(),
+            };
+        }
+        if let Some(s) = find(&find_text, &within_text, start_num) {
+            CalcResult::Number(s as f64)
+        } else {
+            CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "Text not found".to_string(),
+            }
+        }
+    }
+
+    /// Same API as FIND but:
+    ///  * Allows wildcards
+    ///  * It is case insensitive
+    /// SEARCH(find_text, within_text, [start_num])
+    pub(crate) fn fn_search(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() < 2 || args.len() > 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let find_text = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let within_text = match self.get_string(&args[1], cell) {
+            Ok(s) => s,
+            Err(s) => return s,
+        };
+        let start_num = if args.len() == 3 {
+            match self.get_number(&args[2], cell) {
+                Ok(s) => s.floor(),
+                Err(s) => return s,
+            }
+        } else {
+            1.0
+        };
+
+        if start_num < 1.0 {
+            return CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "Start num must be >= 1".to_string(),
+            };
+        }
+        let start_num = start_num as usize;
+
+        if start_num > within_text.len() {
+            return CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "Start num greater than length".to_string(),
+            };
+        }
+        // SEARCH is case insensitive
+        if let Some(s) = search(
+            &find_text.to_lowercase(),
+            &within_text.to_lowercase(),
+            start_num,
+        ) {
+            CalcResult::Number(s as f64)
+        } else {
+            CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "Text not found".to_string(),
+            }
+        }
+    }
+
+    // LEN, LEFT, RIGHT, MID, LOWER, UPPER, TRIM
+    pub(crate) fn fn_len(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            let s = match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::Number(v) => format!("{}", v),
+                CalcResult::String(v) => v,
+                CalcResult::Boolean(b) => {
+                    if b {
+                        "TRUE".to_string()
+                    } else {
+                        "FALSE".to_string()
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::Range { .. } => {
+                    // Implicit Intersection not implemented
+                    return CalcResult::Error {
+                        error: Error::NIMPL,
+                        origin: cell,
+                        message: "Implicit Intersection not implemented".to_string(),
+                    };
+                }
+                CalcResult::EmptyCell | CalcResult::EmptyArg => "".to_string(),
+            };
+            return CalcResult::Number(s.chars().count() as f64);
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+
+    pub(crate) fn fn_trim(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            let s = match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::Number(v) => format!("{}", v),
+                CalcResult::String(v) => v,
+                CalcResult::Boolean(b) => {
+                    if b {
+                        "TRUE".to_string()
+                    } else {
+                        "FALSE".to_string()
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::Range { .. } => {
+                    // Implicit Intersection not implemented
+                    return CalcResult::Error {
+                        error: Error::NIMPL,
+                        origin: cell,
+                        message: "Implicit Intersection not implemented".to_string(),
+                    };
+                }
+                CalcResult::EmptyCell | CalcResult::EmptyArg => "".to_string(),
+            };
+            return CalcResult::String(s.trim().to_owned());
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+
+    pub(crate) fn fn_lower(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            let s = match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::Number(v) => format!("{}", v),
+                CalcResult::String(v) => v,
+                CalcResult::Boolean(b) => {
+                    if b {
+                        "TRUE".to_string()
+                    } else {
+                        "FALSE".to_string()
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::Range { .. } => {
+                    // Implicit Intersection not implemented
+                    return CalcResult::Error {
+                        error: Error::NIMPL,
+                        origin: cell,
+                        message: "Implicit Intersection not implemented".to_string(),
+                    };
+                }
+                CalcResult::EmptyCell | CalcResult::EmptyArg => "".to_string(),
+            };
+            return CalcResult::String(s.to_lowercase());
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+
+    pub(crate) fn fn_upper(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() == 1 {
+            let s = match self.evaluate_node_in_context(&args[0], cell) {
+                CalcResult::Number(v) => format!("{}", v),
+                CalcResult::String(v) => v,
+                CalcResult::Boolean(b) => {
+                    if b {
+                        "TRUE".to_string()
+                    } else {
+                        "FALSE".to_string()
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::Range { .. } => {
+                    // Implicit Intersection not implemented
+                    return CalcResult::Error {
+                        error: Error::NIMPL,
+                        origin: cell,
+                        message: "Implicit Intersection not implemented".to_string(),
+                    };
+                }
+                CalcResult::EmptyCell | CalcResult::EmptyArg => "".to_string(),
+            };
+            return CalcResult::String(s.to_uppercase());
+        }
+        CalcResult::new_args_number_error(cell)
+    }
+
+    pub(crate) fn fn_left(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() > 2 || args.is_empty() {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let s = match self.evaluate_node_in_context(&args[0], cell) {
+            CalcResult::Number(v) => format!("{}", v),
+            CalcResult::String(v) => v,
+            CalcResult::Boolean(b) => {
+                if b {
+                    "TRUE".to_string()
+                } else {
+                    "FALSE".to_string()
+                }
+            }
+            error @ CalcResult::Error { .. } => return error,
+            CalcResult::Range { .. } => {
+                // Implicit Intersection not implemented
+                return CalcResult::Error {
+                    error: Error::NIMPL,
+                    origin: cell,
+                    message: "Implicit Intersection not implemented".to_string(),
+                };
+            }
+            CalcResult::EmptyCell | CalcResult::EmptyArg => "".to_string(),
+        };
+        let num_chars = if args.len() == 2 {
+            match self.evaluate_node_in_context(&args[1], cell) {
+                CalcResult::Number(v) => {
+                    if v < 0.0 {
+                        return CalcResult::Error {
+                            error: Error::VALUE,
+                            origin: cell,
+                            message: "Number must be >= 0".to_string(),
+                        };
+                    }
+                    v.floor() as usize
+                }
+                CalcResult::Boolean(_) | CalcResult::String(_) => {
+                    return CalcResult::Error {
+                        error: Error::VALUE,
+                        origin: cell,
+                        message: "Expecting number".to_string(),
+                    };
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::Range { .. } => {
+                    // Implicit Intersection not implemented
+                    return CalcResult::Error {
+                        error: Error::NIMPL,
+                        origin: cell,
+                        message: "Implicit Intersection not implemented".to_string(),
+                    };
+                }
+                CalcResult::EmptyCell | CalcResult::EmptyArg => 0,
+            }
+        } else {
+            1
+        };
+        let mut result = "".to_string();
+        for (index, ch) in s.chars().enumerate() {
+            if index >= num_chars {
+                break;
+            }
+            result.push(ch);
+        }
+        CalcResult::String(result)
+    }
+
+    pub(crate) fn fn_right(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() > 2 || args.is_empty() {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let s = match self.evaluate_node_in_context(&args[0], cell) {
+            CalcResult::Number(v) => format!("{}", v),
+            CalcResult::String(v) => v,
+            CalcResult::Boolean(b) => {
+                if b {
+                    "TRUE".to_string()
+                } else {
+                    "FALSE".to_string()
+                }
+            }
+            error @ CalcResult::Error { .. } => return error,
+            CalcResult::Range { .. } => {
+                // Implicit Intersection not implemented
+                return CalcResult::Error {
+                    error: Error::NIMPL,
+                    origin: cell,
+                    message: "Implicit Intersection not implemented".to_string(),
+                };
+            }
+            CalcResult::EmptyCell | CalcResult::EmptyArg => "".to_string(),
+        };
+        let num_chars = if args.len() == 2 {
+            match self.evaluate_node_in_context(&args[1], cell) {
+                CalcResult::Number(v) => {
+                    if v < 0.0 {
+                        return CalcResult::Error {
+                            error: Error::VALUE,
+                            origin: cell,
+                            message: "Number must be >= 0".to_string(),
+                        };
+                    }
+                    v.floor() as usize
+                }
+                CalcResult::Boolean(_) | CalcResult::String(_) => {
+                    return CalcResult::Error {
+                        error: Error::VALUE,
+                        origin: cell,
+                        message: "Expecting number".to_string(),
+                    };
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::Range { .. } => {
+                    // Implicit Intersection not implemented
+                    return CalcResult::Error {
+                        error: Error::NIMPL,
+                        origin: cell,
+                        message: "Implicit Intersection not implemented".to_string(),
+                    };
+                }
+                CalcResult::EmptyCell | CalcResult::EmptyArg => 0,
+            }
+        } else {
+            1
+        };
+        let mut result = "".to_string();
+        for (index, ch) in s.chars().rev().enumerate() {
+            if index >= num_chars {
+                break;
+            }
+            result.push(ch);
+        }
+        return CalcResult::String(result.chars().rev().collect::<String>());
+    }
+
+    pub(crate) fn fn_mid(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let s = match self.evaluate_node_in_context(&args[0], cell) {
+            CalcResult::Number(v) => format!("{}", v),
+            CalcResult::String(v) => v,
+            CalcResult::Boolean(b) => {
+                if b {
+                    "TRUE".to_string()
+                } else {
+                    "FALSE".to_string()
+                }
+            }
+            error @ CalcResult::Error { .. } => return error,
+            CalcResult::Range { .. } => {
+                // Implicit Intersection not implemented
+                return CalcResult::Error {
+                    error: Error::NIMPL,
+                    origin: cell,
+                    message: "Implicit Intersection not implemented".to_string(),
+                };
+            }
+            CalcResult::EmptyCell | CalcResult::EmptyArg => "".to_string(),
+        };
+        let start_num = match self.evaluate_node_in_context(&args[1], cell) {
+            CalcResult::Number(v) => {
+                if v < 1.0 {
+                    return CalcResult::Error {
+                        error: Error::VALUE,
+                        origin: cell,
+                        message: "Number must be >= 1".to_string(),
+                    };
+                }
+                v.floor() as usize
+            }
+            error @ CalcResult::Error { .. } => return error,
+            CalcResult::Range { .. } => {
+                // Implicit Intersection not implemented
+                return CalcResult::Error {
+                    error: Error::NIMPL,
+                    origin: cell,
+                    message: "Implicit Intersection not implemented".to_string(),
+                };
+            }
+            _ => {
+                return CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "Expecting number".to_string(),
+                };
+            }
+        };
+        let num_chars = match self.evaluate_node_in_context(&args[2], cell) {
+            CalcResult::Number(v) => {
+                if v < 0.0 {
+                    return CalcResult::Error {
+                        error: Error::VALUE,
+                        origin: cell,
+                        message: "Number must be >= 0".to_string(),
+                    };
+                }
+                v.floor() as usize
+            }
+            CalcResult::String(_) => {
+                return CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "Expecting number".to_string(),
+                };
+            }
+            CalcResult::Boolean(_) => {
+                return CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "Expecting number".to_string(),
+                }
+            }
+            error @ CalcResult::Error { .. } => return error,
+            CalcResult::Range { .. } => {
+                // Implicit Intersection not implemented
+                return CalcResult::Error {
+                    error: Error::NIMPL,
+                    origin: cell,
+                    message: "Implicit Intersection not implemented".to_string(),
+                };
+            }
+            CalcResult::EmptyCell | CalcResult::EmptyArg => 0,
+        };
+        let mut result = "".to_string();
+        let mut count: usize = 0;
+        for (index, ch) in s.chars().enumerate() {
+            if count >= num_chars {
+                break;
+            }
+            if index + 1 >= start_num {
+                result.push(ch);
+                count += 1;
+            }
+        }
+        CalcResult::String(result)
+    }
+
+    // REPT(text, number_times)
+    pub(crate) fn fn_rept(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let text = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let number_times = match self.get_number(&args[1], cell) {
+            Ok(f) => f.floor() as i32,
+            Err(s) => return s,
+        };
+        let text_len = text.len() as i32;
+
+        // We normally don't follow Excel's sometimes archaic size's restrictions
+        // But this might be a security issue
+        if text_len * number_times > 32767 {
+            return CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "number times too high".to_string(),
+            };
+        }
+        if number_times < 0 {
+            return CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "number times too high".to_string(),
+            };
+        }
+        if number_times == 0 {
+            return CalcResult::String("".to_string());
+        }
+        CalcResult::String(text.repeat(number_times as usize))
+    }
+
+    // TEXTAFTER(text, delimiter, [instance_num], [match_mode], [match_end], [if_not_found])
+    pub(crate) fn fn_textafter(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(2..=6).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let text = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let delimiter = match self.get_string(&args[1], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let instance_num = if arg_count > 2 {
+            match self.get_number(&args[2], cell) {
+                Ok(f) => f.floor() as i32,
+                Err(s) => return s,
+            }
+        } else {
+            1
+        };
+        let match_mode = if arg_count > 3 {
+            match self.get_number(&args[3], cell) {
+                Ok(f) => {
+                    if f == 0.0 {
+                        Case::Sensitive
+                    } else {
+                        Case::Insensitive
+                    }
+                }
+                Err(s) => return s,
+            }
+        } else {
+            Case::Sensitive
+        };
+
+        let match_end = if arg_count > 4 {
+            match self.get_number(&args[4], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            // disabled by default
+            // the delimiter is specified in the formula
+            0.0
+        };
+        if instance_num == 0 {
+            return CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "instance_num must be <> 0".to_string(),
+            };
+        }
+        if delimiter.len() > text.len() {
+            // so this is fun(!)
+            // if the function was provided with two arguments is a #VALUE!
+            // if it had more is a #N/A (irrespective of their values)
+            if arg_count > 2 {
+                return CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "The delimiter is longer than the text is trying to match".to_string(),
+                };
+            } else {
+                return CalcResult::Error {
+                    error: Error::NA,
+                    origin: cell,
+                    message: "The delimiter is longer than the text is trying to match".to_string(),
+                };
+            }
+        }
+        if match_end != 0.0 && match_end != 1.0 {
+            return CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "argument must be 0 or 1".to_string(),
+            };
+        };
+        match text_after(&text, &delimiter, instance_num, match_mode) {
+            Some(s) => CalcResult::String(s),
+            None => {
+                if match_end == 1.0 {
+                    if instance_num == 1 {
+                        return CalcResult::String("".to_string());
+                    } else if instance_num == -1 {
+                        return CalcResult::String(text);
+                    }
+                }
+                if arg_count == 6 {
+                    // An empty cell is converted to empty string (not 0)
+                    match self.evaluate_node_in_context(&args[5], cell) {
+                        CalcResult::EmptyCell => CalcResult::String("".to_string()),
+                        result => result,
+                    }
+                } else {
+                    CalcResult::Error {
+                        error: Error::NA,
+                        origin: cell,
+                        message: "Value not found".to_string(),
+                    }
+                }
+            }
+        }
+    }
+
+    pub(crate) fn fn_textbefore(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(2..=6).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let text = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let delimiter = match self.get_string(&args[1], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let instance_num = if arg_count > 2 {
+            match self.get_number(&args[2], cell) {
+                Ok(f) => f.floor() as i32,
+                Err(s) => return s,
+            }
+        } else {
+            1
+        };
+        let match_mode = if arg_count > 3 {
+            match self.get_number(&args[3], cell) {
+                Ok(f) => {
+                    if f == 0.0 {
+                        Case::Sensitive
+                    } else {
+                        Case::Insensitive
+                    }
+                }
+                Err(s) => return s,
+            }
+        } else {
+            Case::Sensitive
+        };
+
+        let match_end = if arg_count > 4 {
+            match self.get_number(&args[4], cell) {
+                Ok(f) => f,
+                Err(s) => return s,
+            }
+        } else {
+            // disabled by default
+            // the delimiter is specified in the formula
+            0.0
+        };
+        if instance_num == 0 {
+            return CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "instance_num must be <> 0".to_string(),
+            };
+        }
+        if delimiter.len() > text.len() {
+            // so this is fun(!)
+            // if the function was provided with two arguments is a #VALUE!
+            // if it had more is a #N/A (irrespective of their values)
+            if arg_count > 2 {
+                return CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "The delimiter is longer than the text is trying to match".to_string(),
+                };
+            } else {
+                return CalcResult::Error {
+                    error: Error::NA,
+                    origin: cell,
+                    message: "The delimiter is longer than the text is trying to match".to_string(),
+                };
+            }
+        }
+        if match_end != 0.0 && match_end != 1.0 {
+            return CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "argument must be 0 or 1".to_string(),
+            };
+        };
+        match text_before(&text, &delimiter, instance_num, match_mode) {
+            Some(s) => CalcResult::String(s),
+            None => {
+                if match_end == 1.0 {
+                    if instance_num == -1 {
+                        return CalcResult::String("".to_string());
+                    } else if instance_num == 1 {
+                        return CalcResult::String(text);
+                    }
+                }
+                if arg_count == 6 {
+                    // An empty cell is converted to empty string (not 0)
+                    match self.evaluate_node_in_context(&args[5], cell) {
+                        CalcResult::EmptyCell => CalcResult::String("".to_string()),
+                        result => result,
+                    }
+                } else {
+                    CalcResult::Error {
+                        error: Error::NA,
+                        origin: cell,
+                        message: "Value not found".to_string(),
+                    }
+                }
+            }
+        }
+    }
+
+    // TEXTJOIN(delimiter, ignore_empty, text1, [text2], …)
+    pub(crate) fn fn_textjoin(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if arg_count < 3 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let delimiter = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let ignore_empty = match self.get_boolean(&args[1], cell) {
+            Ok(b) => b,
+            Err(error) => return error,
+        };
+        let mut values = Vec::new();
+        for arg in &args[2..] {
+            match self.evaluate_node_in_context(arg, cell) {
+                CalcResult::Number(value) => values.push(format!("{value}")),
+                CalcResult::Range { left, right } => {
+                    if left.sheet != right.sheet {
+                        return CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            "Ranges are in different sheets".to_string(),
+                        );
+                    }
+                    let row1 = left.row;
+                    let mut row2 = right.row;
+                    let column1 = left.column;
+                    let mut column2 = right.column;
+                    if row1 == 1 && row2 == LAST_ROW {
+                        row2 = self
+                            .workbook
+                            .worksheet(left.sheet)
+                            .expect("Sheet expected during evaluation.")
+                            .dimension()
+                            .max_row;
+                    }
+                    if column1 == 1 && column2 == LAST_COLUMN {
+                        column2 = self
+                            .workbook
+                            .worksheet(left.sheet)
+                            .expect("Sheet expected during evaluation.")
+                            .dimension()
+                            .max_column;
+                    }
+                    for row in row1..row2 + 1 {
+                        for column in column1..(column2 + 1) {
+                            match self.evaluate_cell(CellReference {
+                                sheet: left.sheet,
+                                row,
+                                column,
+                            }) {
+                                CalcResult::Number(value) => {
+                                    values.push(format!("{value}"));
+                                }
+                                CalcResult::String(value) => values.push(value),
+                                CalcResult::Boolean(value) => {
+                                    if value {
+                                        values.push("TRUE".to_string())
+                                    } else {
+                                        values.push("FALSE".to_string())
+                                    }
+                                }
+                                CalcResult::EmptyCell => {
+                                    if !ignore_empty {
+                                        values.push("".to_string())
+                                    }
+                                }
+                                error @ CalcResult::Error { .. } => return error,
+                                CalcResult::EmptyArg | CalcResult::Range { .. } => {}
+                            }
+                        }
+                    }
+                }
+                error @ CalcResult::Error { .. } => return error,
+                CalcResult::String(value) => values.push(value),
+                CalcResult::Boolean(value) => {
+                    if value {
+                        values.push("TRUE".to_string())
+                    } else {
+                        values.push("FALSE".to_string())
+                    }
+                }
+                CalcResult::EmptyCell => {
+                    if !ignore_empty {
+                        values.push("".to_string())
+                    }
+                }
+                CalcResult::EmptyArg => {}
+            };
+        }
+        let result = values.join(&delimiter);
+        CalcResult::String(result)
+    }
+
+    // SUBSTITUTE(text, old_text, new_text, [instance_num])
+    pub(crate) fn fn_substitute(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if !(2..=4).contains(&arg_count) {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let text = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let old_text = match self.get_string(&args[1], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let new_text = match self.get_string(&args[2], cell) {
+            Ok(s) => s,
+            Err(error) => return error,
+        };
+        let instance_num = if arg_count > 3 {
+            match self.get_number(&args[3], cell) {
+                Ok(f) => Some(f.floor() as i32),
+                Err(s) => return s,
+            }
+        } else {
+            // means every instance is replaced
+            None
+        };
+        if let Some(num) = instance_num {
+            if num < 1 {
+                return CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "Invalid value".to_string(),
+                };
+            }
+            if old_text.is_empty() {
+                return CalcResult::String(text);
+            }
+            CalcResult::String(substitute(&text, &old_text, &new_text, num))
+        } else {
+            if old_text.is_empty() {
+                return CalcResult::String(text);
+            }
+            CalcResult::String(text.replace(&old_text, &new_text))
+        }
+    }
+    pub(crate) fn fn_concatenate(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        let arg_count = args.len();
+        if arg_count == 0 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let mut text_array = Vec::new();
+        for arg in args {
+            let text = match self.get_string(arg, cell) {
+                Ok(s) => s,
+                Err(error) => return error,
+            };
+            text_array.push(text)
+        }
+        CalcResult::String(text_array.join(""))
+    }
+
+    pub(crate) fn fn_exact(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 2 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let result1 = &self.evaluate_node_in_context(&args[0], cell);
+        let result2 = &self.evaluate_node_in_context(&args[1], cell);
+        // FIXME: Implicit intersection
+        if let (CalcResult::Number(number1), CalcResult::Number(number2)) = (result1, result2) {
+            // In Excel two numbers are the same if they are the same up to 15 digits.
+            CalcResult::Boolean(to_precision(*number1, 15) == to_precision(*number2, 15))
+        } else {
+            let string1 = match self.cast_to_string(result1.clone(), cell) {
+                Ok(s) => s,
+                Err(error) => return error,
+            };
+            let string2 = match self.cast_to_string(result2.clone(), cell) {
+                Ok(s) => s,
+                Err(error) => return error,
+            };
+            CalcResult::Boolean(string1 == string2)
+        }
+    }
+    // VALUE(text)
+    pub(crate) fn fn_value(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        match self.evaluate_node_in_context(&args[0], cell) {
+            CalcResult::String(text) => {
+                let currencies = vec!["$", "€"];
+                if let Ok((value, _)) = parse_formatted_number(&text, &currencies) {
+                    return CalcResult::Number(value);
+                };
+                CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "Invalid number".to_string(),
+                }
+            }
+            CalcResult::Number(f) => CalcResult::Number(f),
+            CalcResult::Boolean(_) => CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "Invalid number".to_string(),
+            },
+            error @ CalcResult::Error { .. } => error,
+            CalcResult::Range { .. } => {
+                // TODO Implicit Intersection
+                CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "Invalid number".to_string(),
+                }
+            }
+            CalcResult::EmptyCell | CalcResult::EmptyArg => CalcResult::Number(0.0),
+        }
+    }
+
+    pub(crate) fn fn_t(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        // FIXME: Implicit intersection
+        let result = self.evaluate_node_in_context(&args[0], cell);
+        match result {
+            CalcResult::String(_) => result,
+            error @ CalcResult::Error { .. } => error,
+            _ => CalcResult::String("".to_string()),
+        }
+    }
+
+    // VALUETOTEXT(value)
+    pub(crate) fn fn_valuetotext(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() != 1 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let text = match self.get_string(&args[0], cell) {
+            Ok(s) => s,
+            Err(error) => match error {
+                CalcResult::Error { error, .. } => error.to_string(),
+                _ => "".to_string(),
+            },
+        };
+        CalcResult::String(text)
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/text_util.rs.html b/src/ironcalc_base/functions/text_util.rs.html new file mode 100644 index 0000000..585a85b --- /dev/null +++ b/src/ironcalc_base/functions/text_util.rs.html @@ -0,0 +1,393 @@ +text_util.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+
pub(crate) enum Case {
+    Sensitive,
+    Insensitive,
+}
+
+/// Finds the text after the occurrence instance of 'search_for' in text
+pub(crate) fn text_after(
+    text: &str,
+    delimiter: &str,
+    instance_num: i32,
+    match_mode: Case,
+) -> Option<String> {
+    if let Some((_, right)) = match_text(text, delimiter, instance_num, match_mode) {
+        return Some(text[right..].to_string());
+    };
+    None
+}
+
+pub(crate) fn text_before(
+    text: &str,
+    delimiter: &str,
+    instance_num: i32,
+    match_mode: Case,
+) -> Option<String> {
+    if let Some((left, _)) = match_text(text, delimiter, instance_num, match_mode) {
+        return Some(text[..left].to_string());
+    };
+    None
+}
+
+pub(crate) fn substitute(text: &str, old_text: &str, new_text: &str, instance_num: i32) -> String {
+    if let Some((left, right)) = match_text(text, old_text, instance_num, Case::Sensitive) {
+        return format!("{}{}{}", &text[..left], new_text, &text[right..]);
+    };
+    text.to_string()
+}
+
+fn match_text(
+    text: &str,
+    delimiter: &str,
+    instance_num: i32,
+    match_mode: Case,
+) -> Option<(usize, usize)> {
+    match match_mode {
+        Case::Sensitive => {
+            if instance_num > 0 {
+                text_sensitive(text, delimiter, instance_num)
+            } else {
+                text_sensitive_reverse(text, delimiter, -instance_num)
+            }
+        }
+        Case::Insensitive => {
+            if instance_num > 0 {
+                text_sensitive(
+                    &text.to_lowercase(),
+                    &delimiter.to_lowercase(),
+                    instance_num,
+                )
+            } else {
+                text_sensitive_reverse(
+                    &text.to_lowercase(),
+                    &delimiter.to_lowercase(),
+                    -instance_num,
+                )
+            }
+        }
+    }
+}
+
+fn text_sensitive(text: &str, delimiter: &str, instance_num: i32) -> Option<(usize, usize)> {
+    let mut byte_index = 0;
+    let mut local_index = 1;
+    // delimiter length in bytes
+    let delimiter_len = delimiter.len();
+    for c in text.chars() {
+        if text[byte_index..].starts_with(delimiter) {
+            if local_index == instance_num {
+                return Some((byte_index, byte_index + delimiter_len));
+            } else {
+                local_index += 1;
+            }
+        }
+        byte_index += c.len_utf8();
+    }
+    None
+}
+
+fn text_sensitive_reverse(
+    text: &str,
+    delimiter: &str,
+    instance_num: i32,
+) -> Option<(usize, usize)> {
+    let text_len = text.len();
+    let mut byte_index = text_len;
+    let mut local_index = 1;
+    let delimiter_len = delimiter.len();
+    for c in text.chars().rev() {
+        if text[byte_index..].starts_with(delimiter) {
+            if local_index == instance_num {
+                return Some((byte_index, byte_index + delimiter_len));
+            } else {
+                local_index += 1;
+            }
+        }
+
+        byte_index -= c.len_utf8();
+    }
+    None
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::functions::text_util::Case;
+
+    use super::{text_after, text_before};
+    #[test]
+    fn test_text_after_sensitive() {
+        assert_eq!(
+            text_after("One element", "ele", 1, Case::Sensitive),
+            Some("ment".to_string())
+        );
+        assert_eq!(
+            text_after("One element", "e", 1, Case::Sensitive),
+            Some(" element".to_string())
+        );
+        assert_eq!(
+            text_after("One element", "e", 4, Case::Sensitive),
+            Some("nt".to_string())
+        );
+        assert_eq!(text_after("One element", "e", 5, Case::Sensitive), None);
+        assert_eq!(
+            text_after("長壽相等!", "相", 1, Case::Sensitive),
+            Some("等!".to_string())
+        );
+    }
+    #[test]
+    fn test_text_before_sensitive() {
+        assert_eq!(
+            text_before("One element", "ele", 1, Case::Sensitive),
+            Some("One ".to_string())
+        );
+        assert_eq!(
+            text_before("One element", "e", 1, Case::Sensitive),
+            Some("On".to_string())
+        );
+        assert_eq!(
+            text_before("One element", "e", 4, Case::Sensitive),
+            Some("One elem".to_string())
+        );
+        assert_eq!(text_before("One element", "e", 5, Case::Sensitive), None);
+        assert_eq!(
+            text_before("長壽相等!", "相", 1, Case::Sensitive),
+            Some("長壽".to_string())
+        );
+    }
+    #[test]
+    fn test_text_after_insensitive() {
+        assert_eq!(
+            text_after("One element", "eLe", 1, Case::Insensitive),
+            Some("ment".to_string())
+        );
+        assert_eq!(
+            text_after("One element", "E", 1, Case::Insensitive),
+            Some(" element".to_string())
+        );
+        assert_eq!(
+            text_after("One element", "E", 4, Case::Insensitive),
+            Some("nt".to_string())
+        );
+        assert_eq!(text_after("One element", "E", 5, Case::Insensitive), None);
+        assert_eq!(
+            text_after("長壽相等!", "相", 1, Case::Insensitive),
+            Some("等!".to_string())
+        );
+    }
+    #[test]
+    fn test_text_before_insensitive() {
+        assert_eq!(
+            text_before("One element", "eLe", 1, Case::Insensitive),
+            Some("One ".to_string())
+        );
+        assert_eq!(
+            text_before("One element", "E", 1, Case::Insensitive),
+            Some("On".to_string())
+        );
+        assert_eq!(
+            text_before("One element", "E", 4, Case::Insensitive),
+            Some("One elem".to_string())
+        );
+        assert_eq!(text_before("One element", "E", 5, Case::Insensitive), None);
+        assert_eq!(
+            text_before("長壽相等!", "相", 1, Case::Insensitive),
+            Some("長壽".to_string())
+        );
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/util.rs.html b/src/ironcalc_base/functions/util.rs.html new file mode 100644 index 0000000..1815705 --- /dev/null +++ b/src/ironcalc_base/functions/util.rs.html @@ -0,0 +1,803 @@ +util.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+
use regex::{escape, Regex};
+
+use crate::{calc_result::CalcResult, expressions::token::is_english_error_string};
+
+/// This test for exact match (modulo case).
+///   * strings are not cast into bools or numbers
+///   * empty cell is not cast into empty string or zero
+pub(crate) fn values_are_equal(left: &CalcResult, right: &CalcResult) -> bool {
+    match (left, right) {
+        (CalcResult::Number(value1), CalcResult::Number(value2)) => {
+            if (value2 - value1).abs() < f64::EPSILON {
+                return true;
+            }
+            false
+        }
+        (CalcResult::String(value1), CalcResult::String(value2)) => {
+            let value1 = value1.to_uppercase();
+            let value2 = value2.to_uppercase();
+            value1 == value2
+        }
+        (CalcResult::Boolean(value1), CalcResult::Boolean(value2)) => value1 == value2,
+        (CalcResult::EmptyCell, CalcResult::EmptyCell) => true,
+        // NOTE: Errors and Ranges are not covered
+        (_, _) => false,
+    }
+}
+
+/// In Excel there are two ways of comparing cell values.
+/// The old school comparison valid in formulas like D3 < D4 or HLOOKUP,... cast empty cells into empty strings or 0
+/// For the new formulas like XLOOKUP or SORT an empty cell is always larger than anything else.
+
+// ..., -2, -1, 0, 1, 2, ..., A-Z, FALSE, TRUE;
+pub(crate) fn compare_values(left: &CalcResult, right: &CalcResult) -> i32 {
+    match (left, right) {
+        (CalcResult::Number(value1), CalcResult::Number(value2)) => {
+            if (value2 - value1).abs() < f64::EPSILON {
+                return 0;
+            }
+            if value1 < value2 {
+                return -1;
+            }
+            1
+        }
+        (CalcResult::Number(_value1), CalcResult::String(_value2)) => -1,
+        (CalcResult::Number(_value1), CalcResult::Boolean(_value2)) => -1,
+        (CalcResult::String(value1), CalcResult::String(value2)) => {
+            let value1 = value1.to_uppercase();
+            let value2 = value2.to_uppercase();
+            match value1.cmp(&value2) {
+                std::cmp::Ordering::Less => -1,
+                std::cmp::Ordering::Equal => 0,
+                std::cmp::Ordering::Greater => 1,
+            }
+        }
+        (CalcResult::String(_value1), CalcResult::Boolean(_value2)) => -1,
+        (CalcResult::Boolean(value1), CalcResult::Boolean(value2)) => {
+            if value1 == value2 {
+                return 0;
+            }
+            if *value1 {
+                return 1;
+            }
+            -1
+        }
+        (CalcResult::EmptyCell, CalcResult::String(_value2)) => {
+            compare_values(&CalcResult::String("".to_string()), right)
+        }
+        (CalcResult::String(_value1), CalcResult::EmptyCell) => {
+            compare_values(left, &CalcResult::String("".to_string()))
+        }
+        (CalcResult::EmptyCell, CalcResult::Number(_value2)) => {
+            compare_values(&CalcResult::Number(0.0), right)
+        }
+        (CalcResult::Number(_value1), CalcResult::EmptyCell) => {
+            compare_values(left, &CalcResult::Number(0.0))
+        }
+        (CalcResult::EmptyCell, CalcResult::EmptyCell) => 0,
+        // NOTE: Errors and Ranges are not covered
+        (_, _) => 1,
+    }
+}
+
+/// We convert an Excel wildcard into a Rust (Perl family) regex
+pub(crate) fn from_wildcard_to_regex(
+    wildcard: &str,
+    exact: bool,
+) -> Result<regex::Regex, regex::Error> {
+    // 1. Escape all
+    let reg = &escape(wildcard);
+
+    // 2. We convert the escaped '?' into '.' (matches a single character)
+    let reg = &reg.replace("\\?", ".");
+    // 3. We convert the escaped '*' into '.*' (matches anything)
+    let reg = &reg.replace("\\*", ".*");
+
+    // 4. We send '\\~\\~' to '??' that is an unescaped regular expression, therefore cannot be in reg
+    let reg = &reg.replace("\\~\\~", "??");
+
+    // 5. If the escaped and converted '*' is preceded by '~' then it's a raw '*'
+    let reg = &reg.replace("\\~.*", "\\*");
+    // 6. If the escaped and converted '.' is preceded by '~' then it's a raw '?'
+    let reg = &reg.replace("\\~.", "\\?");
+    // '~' is used in Excel to escape any other character.
+    //    So ~x goes to x (whatever x is)
+    // 7. Remove all the others '\\~d' --> 'd'
+    let reg = &reg.replace("\\~", "");
+    // 8. Put back the '\\~\\~'  as '\\~'
+    let reg = &reg.replace("??", "\\~");
+
+    // And we have a valid Perl regex! (As Kim Kardashian said before me: "I know, right?")
+    if exact {
+        return Regex::new(&format!("^{}$", reg));
+    }
+    Regex::new(reg)
+}
+
+/// NUMBERS ///
+///*********///
+
+// It could be either the number or a string representation of the number
+// In the rest of the cases calc_result needs to be a number (cannot be the string "23", for instance)
+fn result_is_equal_to_number(calc_result: &CalcResult, target: f64) -> bool {
+    match calc_result {
+        CalcResult::Number(f) => {
+            if (f - target).abs() < f64::EPSILON {
+                return true;
+            }
+            false
+        }
+        CalcResult::String(s) => {
+            if let Ok(f) = s.parse::<f64>() {
+                if (f - target).abs() < f64::EPSILON {
+                    return true;
+                }
+                return false;
+            }
+            false
+        }
+        _ => false,
+    }
+}
+
+fn result_is_less_than_number(calc_result: &CalcResult, target: f64) -> bool {
+    match calc_result {
+        CalcResult::Number(f) => *f < target,
+        _ => false,
+    }
+}
+
+fn result_is_less_or_equal_than_number(calc_result: &CalcResult, target: f64) -> bool {
+    match calc_result {
+        CalcResult::Number(f) => *f <= target,
+        _ => false,
+    }
+}
+
+fn result_is_greater_than_number(calc_result: &CalcResult, target: f64) -> bool {
+    match calc_result {
+        CalcResult::Number(f) => *f > target,
+        _ => false,
+    }
+}
+
+fn result_is_greater_or_equal_than_number(calc_result: &CalcResult, target: f64) -> bool {
+    match calc_result {
+        CalcResult::Number(f) => *f >= target,
+        _ => false,
+    }
+}
+
+fn result_is_not_equal_to_number(calc_result: &CalcResult, target: f64) -> bool {
+    match calc_result {
+        CalcResult::Number(f) => {
+            if (f - target).abs() > f64::EPSILON {
+                return true;
+            }
+            false
+        }
+        _ => true,
+    }
+}
+
+/// BOOLEANS ///
+///**********///
+
+// Booleans have to be "exactly" equal
+fn result_is_equal_to_bool(calc_result: &CalcResult, target: bool) -> bool {
+    match calc_result {
+        CalcResult::Boolean(f) => target == *f,
+        _ => false,
+    }
+}
+
+fn result_is_not_equal_to_bool(calc_result: &CalcResult, target: bool) -> bool {
+    match calc_result {
+        CalcResult::Boolean(f) => target != *f,
+        _ => true,
+    }
+}
+
+/// STRINGS ///
+///*********///
+
+/// Note that strings are case insensitive. `target` must always be lower case.
+
+pub(crate) fn result_matches_regex(calc_result: &CalcResult, reg: &Regex) -> bool {
+    match calc_result {
+        CalcResult::String(s) => reg.is_match(&s.to_lowercase()),
+        _ => false,
+    }
+}
+
+fn result_is_equal_to_string(calc_result: &CalcResult, target: &str) -> bool {
+    match calc_result {
+        CalcResult::String(s) => {
+            if target == s.to_lowercase() {
+                return true;
+            }
+            false
+        }
+        CalcResult::EmptyCell => target.is_empty(),
+        _ => false,
+    }
+}
+
+fn result_is_not_equal_to_string(calc_result: &CalcResult, target: &str) -> bool {
+    match calc_result {
+        CalcResult::String(s) => {
+            if target != s.to_lowercase() {
+                return true;
+            }
+            false
+        }
+        _ => false,
+    }
+}
+
+fn result_is_less_than_string(calc_result: &CalcResult, target: &str) -> bool {
+    match calc_result {
+        CalcResult::String(s) => target.cmp(&s.to_lowercase()) == std::cmp::Ordering::Greater,
+        _ => false,
+    }
+}
+
+fn result_is_less_or_equal_than_string(calc_result: &CalcResult, target: &str) -> bool {
+    match calc_result {
+        CalcResult::String(s) => {
+            let lower_case = &s.to_lowercase();
+            target.cmp(lower_case) == std::cmp::Ordering::Less || lower_case == target
+        }
+        _ => false,
+    }
+}
+
+fn result_is_greater_than_string(calc_result: &CalcResult, target: &str) -> bool {
+    match calc_result {
+        CalcResult::String(s) => target.cmp(&s.to_lowercase()) == std::cmp::Ordering::Less,
+        _ => false,
+    }
+}
+
+fn result_is_greater_or_equal_than_string(calc_result: &CalcResult, target: &str) -> bool {
+    match calc_result {
+        CalcResult::String(s) => {
+            let lower_case = &s.to_lowercase();
+            target.cmp(lower_case) == std::cmp::Ordering::Greater || lower_case == target
+        }
+        _ => false,
+    }
+}
+
+/// ERRORS ///
+///********///
+
+fn result_is_equal_to_error(calc_result: &CalcResult, target: &str) -> bool {
+    match calc_result {
+        CalcResult::Error { error, .. } => target == error.to_string(),
+        _ => false,
+    }
+}
+
+fn result_is_not_equal_to_error(calc_result: &CalcResult, target: &str) -> bool {
+    match calc_result {
+        CalcResult::Error { error, .. } => target != error.to_string(),
+        _ => true,
+    }
+}
+
+/// EMPTY ///
+///*******///
+
+// Note that these two are not inverse of each other.
+// In particular, you can never match an empty cell.
+
+fn result_is_not_equal_to_empty(calc_result: &CalcResult) -> bool {
+    !matches!(calc_result, CalcResult::EmptyCell)
+}
+
+fn result_is_equal_to_empty(calc_result: &CalcResult) -> bool {
+    match calc_result {
+        CalcResult::Number(f) => (f - 0.0).abs() < f64::EPSILON,
+        _ => false,
+    }
+}
+
+/// This returns a function (closure) of signature fn(&CalcResult) -> bool
+/// It is Boxed because it returns different closures, so the size cannot be known at compile time
+/// The lifetime (a) of value has to be longer or equal to the lifetime of the returned closure
+pub(crate) fn build_criteria<'a>(value: &'a CalcResult) -> Box<dyn Fn(&CalcResult) -> bool + 'a> {
+    match value {
+        CalcResult::String(s) => {
+            if let Some(v) = s.strip_prefix("<=") {
+                // TODO: I am not implementing <= ERROR or <= BOOLEAN
+                if let Ok(f) = v.parse::<f64>() {
+                    Box::new(move |x| result_is_less_or_equal_than_number(x, f))
+                } else if v.is_empty() {
+                    Box::new(move |_x| false)
+                } else {
+                    Box::new(move |x| result_is_less_or_equal_than_string(x, &v.to_lowercase()))
+                }
+            } else if let Some(v) = s.strip_prefix(">=") {
+                // TODO: I am not implementing >= ERROR or >= BOOLEAN
+                if let Ok(f) = v.parse::<f64>() {
+                    Box::new(move |x| result_is_greater_or_equal_than_number(x, f))
+                } else if v.is_empty() {
+                    Box::new(move |_x| false)
+                } else {
+                    Box::new(move |x| result_is_greater_or_equal_than_string(x, &v.to_lowercase()))
+                }
+            } else if let Some(v) = s.strip_prefix("<>") {
+                if let Ok(f) = v.parse::<f64>() {
+                    Box::new(move |x| result_is_not_equal_to_number(x, f))
+                } else if let Ok(b) = v.to_lowercase().parse::<bool>() {
+                    Box::new(move |x| result_is_not_equal_to_bool(x, b))
+                } else if is_english_error_string(v) {
+                    Box::new(move |x| result_is_not_equal_to_error(x, v))
+                } else if v.contains('*') || v.contains('?') {
+                    if let Ok(reg) = from_wildcard_to_regex(&v.to_lowercase(), true) {
+                        Box::new(move |x| !result_matches_regex(x, &reg))
+                    } else {
+                        Box::new(move |_| false)
+                    }
+                } else if v.is_empty() {
+                    Box::new(result_is_not_equal_to_empty)
+                } else {
+                    Box::new(move |x| result_is_not_equal_to_string(x, &v.to_lowercase()))
+                }
+            } else if let Some(v) = s.strip_prefix('<') {
+                // TODO: I am not implementing < ERROR or < BOOLEAN
+                if let Ok(f) = v.parse::<f64>() {
+                    Box::new(move |x| result_is_less_than_number(x, f))
+                } else if v.is_empty() {
+                    Box::new(move |_x| false)
+                } else {
+                    Box::new(move |x| result_is_less_than_string(x, &v.to_lowercase()))
+                }
+            } else if let Some(v) = s.strip_prefix('>') {
+                // TODO: I am not implementing > ERROR or > BOOLEAN
+                if let Ok(f) = v.parse::<f64>() {
+                    Box::new(move |x| result_is_greater_than_number(x, f))
+                } else if v.is_empty() {
+                    Box::new(move |_x| false)
+                } else {
+                    Box::new(move |x| result_is_greater_than_string(x, &v.to_lowercase()))
+                }
+            } else {
+                let v = if let Some(a) = s.strip_prefix('=') {
+                    a
+                } else {
+                    s
+                };
+                if let Ok(f) = v.parse::<f64>() {
+                    Box::new(move |x| result_is_equal_to_number(x, f))
+                } else if let Ok(b) = v.to_lowercase().parse::<bool>() {
+                    Box::new(move |x| result_is_equal_to_bool(x, b))
+                } else if is_english_error_string(v) {
+                    Box::new(move |x| result_is_equal_to_error(x, v))
+                } else if v.contains('*') || v.contains('?') {
+                    if let Ok(reg) = from_wildcard_to_regex(&v.to_lowercase(), true) {
+                        Box::new(move |x| result_matches_regex(x, &reg))
+                    } else {
+                        Box::new(move |_| false)
+                    }
+                } else {
+                    Box::new(move |x| result_is_equal_to_string(x, &v.to_lowercase()))
+                }
+            }
+        }
+        CalcResult::Number(target) => Box::new(move |x| result_is_equal_to_number(x, *target)),
+        CalcResult::Boolean(b) => Box::new(move |x| result_is_equal_to_bool(x, *b)),
+        CalcResult::Error { error, .. } => {
+            // An error will match an error (never a string that is an error)
+            Box::new(move |x| result_is_equal_to_error(x, &error.to_string()))
+        }
+        CalcResult::Range { left: _, right: _ } => {
+            // TODO: Implicit Intersection
+            Box::new(move |_x| false)
+        }
+        CalcResult::EmptyCell | CalcResult::EmptyArg => Box::new(result_is_equal_to_empty),
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/functions/xlookup.rs.html b/src/ironcalc_base/functions/xlookup.rs.html new file mode 100644 index 0000000..854ebae --- /dev/null +++ b/src/ironcalc_base/functions/xlookup.rs.html @@ -0,0 +1,769 @@ +xlookup.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+
use crate::constants::{LAST_COLUMN, LAST_ROW};
+use crate::{
+    calc_result::{CalcResult, CellReference},
+    expressions::parser::Node,
+    expressions::token::Error,
+    model::Model,
+};
+
+use super::{
+    binary_search::{
+        binary_search_descending_or_greater, binary_search_descending_or_smaller,
+        binary_search_or_greater, binary_search_or_smaller,
+    },
+    util::{compare_values, from_wildcard_to_regex, result_matches_regex},
+};
+
+#[derive(PartialEq)]
+enum SearchMode {
+    StartAtFirstItem = 1,
+    StartAtLastItem = -1,
+    BinarySearchDescending = -2,
+    BinarySearchAscending = 2,
+}
+
+#[derive(PartialEq)]
+enum MatchMode {
+    ExactMatchSmaller = -1,
+    ExactMatch = 0,
+    ExactMatchLarger = 1,
+    WildcardMatch = 2,
+}
+
+// lookup_value in array, match_mode search_mode
+fn linear_search(
+    lookup_value: &CalcResult,
+    array: &[CalcResult],
+    search_mode: SearchMode,
+    match_mode: MatchMode,
+) -> Option<usize> {
+    let length = array.len();
+
+    match match_mode {
+        MatchMode::ExactMatch => {
+            // exact match
+            for l in 0..length {
+                let index = if search_mode == SearchMode::StartAtFirstItem {
+                    l
+                } else {
+                    length - l - 1
+                };
+
+                let value = &array[index];
+                if compare_values(value, lookup_value) == 0 {
+                    return Some(index);
+                }
+            }
+            return None;
+        }
+        MatchMode::ExactMatchSmaller | MatchMode::ExactMatchLarger => {
+            // exact match, if none found return the next smaller/larger item
+            let mut found_index = 0;
+            let mut approx = None;
+            let m_mode = match_mode as i32;
+            for l in 0..length {
+                let index = if search_mode == SearchMode::StartAtFirstItem {
+                    l
+                } else {
+                    length - l - 1
+                };
+
+                let value = &array[index];
+                let c = compare_values(value, lookup_value);
+                if c == 0 {
+                    return Some(index);
+                } else if c == m_mode {
+                    match approx {
+                        None => {
+                            approx = Some(value.clone());
+                            found_index = index;
+                        }
+                        Some(ref p) => {
+                            if compare_values(p, value) == m_mode {
+                                approx = Some(value.clone());
+                                found_index = index;
+                            }
+                        }
+                    }
+                }
+            }
+            if approx.is_none() {
+                return None;
+            } else {
+                return Some(found_index);
+            }
+        }
+        MatchMode::WildcardMatch => {
+            let result_matches: Box<dyn Fn(&CalcResult) -> bool> =
+                if let CalcResult::String(s) = &lookup_value {
+                    if let Ok(reg) = from_wildcard_to_regex(&s.to_lowercase(), true) {
+                        Box::new(move |x| result_matches_regex(x, &reg))
+                    } else {
+                        Box::new(move |_| false)
+                    }
+                } else {
+                    Box::new(move |x| compare_values(x, lookup_value) == 0)
+                };
+            for l in 0..length {
+                let index = if search_mode == SearchMode::StartAtFirstItem {
+                    l
+                } else {
+                    length - l - 1
+                };
+                let value = &array[index];
+                if result_matches(value) {
+                    return Some(index);
+                }
+            }
+        }
+    }
+    None
+}
+
+impl Model {
+    /// The XLOOKUP function searches a range or an array, and then returns the item corresponding
+    /// to the first match it finds. If no match exists, then XLOOKUP can return the closest (approximate) match.
+    /// =XLOOKUP(lookup_value, lookup_array, return_array, [if_not_found], [match_mode], [search_mode])
+    ///
+    /// lookup_array and return_array must be column or row arrays and of the same dimension.
+    /// Otherwise #VALUE! is returned
+    /// [if_not_found]
+    /// Where a valid match is not found, return the [if_not_found] text you supply.
+    /// If a valid match is not found, and [if_not_found] is missing, #N/A is returned.
+    ///
+    /// [match_mode]
+    /// Specify the match type:
+    ///   *  0 - Exact match. If none found, return #N/A. This is the default.
+    ///   * -1 - Exact match. If none found, return the next smaller item.
+    ///   *  1 - Exact match. If none found, return the next larger item.
+    ///   *  2 - A wildcard match where *, ?, and ~ have special meaning.
+    ///
+    /// [search_mode]
+    /// Specify the search mode to use:
+    ///   *  1 - Perform a search starting at the first item. This is the default.
+    ///   * -1 - Perform a reverse search starting at the last item.
+    ///   *  2 - Perform a binary search that relies on lookup_array being sorted
+    ///          in ascending order. If not sorted, invalid results will be returned.
+    ///   * -2 - Perform a binary search that relies on lookup_array being sorted
+    ///          in descending order. If not sorted, invalid results will be returned.
+    pub(crate) fn fn_xlookup(&mut self, args: &[Node], cell: CellReference) -> CalcResult {
+        if args.len() < 3 || args.len() > 6 {
+            return CalcResult::new_args_number_error(cell);
+        }
+        let lookup_value = self.evaluate_node_in_context(&args[0], cell);
+        if lookup_value.is_error() {
+            return lookup_value;
+        }
+        // Get optional arguments
+        let if_not_found = if args.len() >= 4 {
+            let v = self.evaluate_node_in_context(&args[3], cell);
+            match v {
+                CalcResult::EmptyArg => CalcResult::Error {
+                    error: Error::NA,
+                    origin: cell,
+                    message: "Not found".to_string(),
+                },
+                _ => v,
+            }
+        } else {
+            // default
+            CalcResult::Error {
+                error: Error::NA,
+                origin: cell,
+                message: "Not found".to_string(),
+            }
+        };
+        let match_mode = if args.len() >= 5 {
+            match self.get_number(&args[4], cell) {
+                Ok(c) => match c.floor() as i32 {
+                    -1 => MatchMode::ExactMatchSmaller,
+                    1 => MatchMode::ExactMatchLarger,
+                    0 => MatchMode::ExactMatch,
+                    2 => MatchMode::WildcardMatch,
+                    _ => {
+                        return CalcResult::Error {
+                            error: Error::VALUE,
+                            origin: cell,
+                            message: "Unexpected number".to_string(),
+                        };
+                    }
+                },
+                Err(s) => return s,
+            }
+        } else {
+            // default
+            MatchMode::ExactMatch
+        };
+        let search_mode = if args.len() == 6 {
+            match self.get_number(&args[5], cell) {
+                Ok(c) => match c.floor() as i32 {
+                    1 => SearchMode::StartAtFirstItem,
+                    -1 => SearchMode::StartAtLastItem,
+                    -2 => SearchMode::BinarySearchDescending,
+                    2 => SearchMode::BinarySearchAscending,
+                    _ => {
+                        return CalcResult::Error {
+                            error: Error::ERROR,
+                            origin: cell,
+                            message: "Unexpected number".to_string(),
+                        };
+                    }
+                },
+                Err(s) => return s,
+            }
+        } else {
+            // default
+            SearchMode::StartAtFirstItem
+        };
+        // lookup_array
+        match self.evaluate_node_in_context(&args[1], cell) {
+            CalcResult::Range { left, right } => {
+                let is_row_vector;
+                if left.row == right.row {
+                    is_row_vector = false;
+                } else if left.column == right.column {
+                    is_row_vector = true;
+                } else {
+                    // second argument must be a vector
+                    return CalcResult::Error {
+                        error: Error::ERROR,
+                        origin: cell,
+                        message: "Second argument must be a vector".to_string(),
+                    };
+                }
+                // return array
+                match self.evaluate_node_in_context(&args[2], cell) {
+                    CalcResult::Range {
+                        left: result_left,
+                        right: result_right,
+                    } => {
+                        if result_right.row - result_left.row != right.row - left.row
+                            || result_right.column - result_left.column
+                                != right.column - left.column
+                        {
+                            return CalcResult::Error {
+                                error: Error::VALUE,
+                                origin: cell,
+                                message: "Arrays must be of the same size".to_string(),
+                            };
+                        }
+                        let mut row2 = right.row;
+                        let row1 = left.row;
+                        let mut column2 = right.column;
+                        let column1 = left.column;
+
+                        if row1 == 1 && row2 == LAST_ROW {
+                            row2 = self
+                                .workbook
+                                .worksheet(left.sheet)
+                                .expect("Sheet expected during evaluation.")
+                                .dimension()
+                                .max_row;
+                        }
+                        if column1 == 1 && column2 == LAST_COLUMN {
+                            column2 = self
+                                .workbook
+                                .worksheet(left.sheet)
+                                .expect("Sheet expected during evaluation.")
+                                .dimension()
+                                .max_column;
+                        }
+                        let left = CellReference {
+                            sheet: left.sheet,
+                            column: column1,
+                            row: row1,
+                        };
+                        let right = CellReference {
+                            sheet: left.sheet,
+                            column: column2,
+                            row: row2,
+                        };
+                        match search_mode {
+                            SearchMode::StartAtFirstItem | SearchMode::StartAtLastItem => {
+                                let array = &self.prepare_array(&left, &right, is_row_vector);
+                                match linear_search(&lookup_value, array, search_mode, match_mode) {
+                                    Some(index) => {
+                                        let row_index =
+                                            if is_row_vector { index as i32 } else { 0 };
+                                        let column_index =
+                                            if is_row_vector { 0 } else { index as i32 };
+                                        self.evaluate_cell(CellReference {
+                                            sheet: result_left.sheet,
+                                            row: result_left.row + row_index,
+                                            column: result_left.column + column_index,
+                                        })
+                                    }
+                                    None => if_not_found,
+                                }
+                            }
+                            SearchMode::BinarySearchAscending
+                            | SearchMode::BinarySearchDescending => {
+                                let index = if match_mode == MatchMode::ExactMatchLarger {
+                                    if search_mode == SearchMode::BinarySearchAscending {
+                                        binary_search_or_greater(
+                                            &lookup_value,
+                                            &self.prepare_array(&left, &right, is_row_vector),
+                                        )
+                                    } else {
+                                        binary_search_descending_or_greater(
+                                            &lookup_value,
+                                            &self.prepare_array(&left, &right, is_row_vector),
+                                        )
+                                    }
+                                } else if search_mode == SearchMode::BinarySearchAscending {
+                                    binary_search_or_smaller(
+                                        &lookup_value,
+                                        &self.prepare_array(&left, &right, is_row_vector),
+                                    )
+                                } else {
+                                    binary_search_descending_or_smaller(
+                                        &lookup_value,
+                                        &self.prepare_array(&left, &right, is_row_vector),
+                                    )
+                                };
+                                match index {
+                                    None => if_not_found,
+                                    Some(l) => {
+                                        let row =
+                                            result_left.row + if is_row_vector { l } else { 0 };
+                                        let column =
+                                            result_left.column + if is_row_vector { 0 } else { l };
+                                        if match_mode == MatchMode::ExactMatch {
+                                            let value = self.evaluate_cell(CellReference {
+                                                sheet: left.sheet,
+                                                row: left.row + if is_row_vector { l } else { 0 },
+                                                column: left.column
+                                                    + if is_row_vector { 0 } else { l },
+                                            });
+                                            if compare_values(&value, &lookup_value) == 0 {
+                                                self.evaluate_cell(CellReference {
+                                                    sheet: result_left.sheet,
+                                                    row,
+                                                    column,
+                                                })
+                                            } else {
+                                                if_not_found
+                                            }
+                                        } else if match_mode == MatchMode::ExactMatchSmaller
+                                            || match_mode == MatchMode::ExactMatchLarger
+                                        {
+                                            self.evaluate_cell(CellReference {
+                                                sheet: result_left.sheet,
+                                                row,
+                                                column,
+                                            })
+                                        } else {
+                                            CalcResult::Error {
+                                                error: Error::VALUE,
+                                                origin: cell,
+                                                message: "Cannot use wildcard in binary search"
+                                                    .to_string(),
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    error @ CalcResult::Error { .. } => error,
+                    _ => CalcResult::Error {
+                        error: Error::VALUE,
+                        origin: cell,
+                        message: "Range expected".to_string(),
+                    },
+                }
+            }
+            error @ CalcResult::Error { .. } => error,
+            _ => CalcResult::Error {
+                error: Error::NA,
+                origin: cell,
+                message: "Range expected".to_string(),
+            },
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/implicit_intersection.rs.html b/src/ironcalc_base/implicit_intersection.rs.html new file mode 100644 index 0000000..0274494 --- /dev/null +++ b/src/ironcalc_base/implicit_intersection.rs.html @@ -0,0 +1,97 @@ +implicit_intersection.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+
use crate::calc_result::{CellReference, Range};
+
+/// It returns the closest cell from cell_reference to range in the same column/row
+/// Examples
+///  * i_i(B5, A2:A9) -> B5
+///  * i_i(B5, A7:A9) -> None
+///  * i_i(B5, A2:D2) -> B2
+pub(crate) fn implicit_intersection(
+    cell_reference: &CellReference,
+    range: &Range,
+) -> Option<CellReference> {
+    let left = &range.left;
+    let right = &range.right;
+    let sheet = cell_reference.sheet;
+    // If they are not all in the same sheet there is no intersection
+    if sheet != left.sheet && sheet != right.sheet {
+        return None;
+    }
+    let row = cell_reference.row;
+    let column = cell_reference.column;
+    if row >= left.row && row <= right.row {
+        if left.column != right.column {
+            return None;
+        }
+        return Some(CellReference {
+            sheet,
+            row,
+            column: left.column,
+        });
+    } else if column >= left.column && column <= right.column {
+        if left.row != right.row {
+            return None;
+        }
+        return Some(CellReference {
+            sheet,
+            row: left.row,
+            column,
+        });
+    } else if left.row == right.row && left.column == right.column {
+        // If the range is a single cell, then return it.
+        return Some(CellReference {
+            sheet,
+            row: left.row,
+            column: right.column,
+        });
+    }
+    None
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/language/mod.rs.html b/src/ironcalc_base/language/mod.rs.html new file mode 100644 index 0000000..73e7648 --- /dev/null +++ b/src/ironcalc_base/language/mod.rs.html @@ -0,0 +1,93 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+
use once_cell::sync::Lazy;
+use serde::{Deserialize, Serialize};
+
+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,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Errors {
+    #[serde(rename = "ref")]
+    pub ref_value: String,
+    pub name: String,
+    pub value: String,
+    pub div: String,
+    pub na: String,
+    pub num: String,
+    pub nimpl: String,
+    pub spill: String,
+    pub calc: String,
+    pub circ: String,
+    pub error: String,
+    pub null: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Language {
+    pub booleans: Booleans,
+    pub errors: Errors,
+}
+
+static LANGUAGES: Lazy<HashMap<String, Language>> = Lazy::new(|| {
+    serde_json::from_str(include_str!("language.json")).expect("Failed parsing language file")
+});
+
+pub fn get_language(id: &str) -> Result<&Language, String> {
+    let language = LANGUAGES
+        .get(id)
+        .ok_or(format!("Language is not supported: '{}'", id))?;
+    Ok(language)
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/lib.rs.html b/src/ironcalc_base/lib.rs.html new file mode 100644 index 0000000..f1ecfa8 --- /dev/null +++ b/src/ironcalc_base/lib.rs.html @@ -0,0 +1,65 @@ +lib.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+
#![deny(clippy::unwrap_used)]
+pub mod calc_result;
+pub mod cell;
+pub mod expressions;
+pub mod formatter;
+pub mod language;
+pub mod locale;
+pub mod model;
+pub mod new_empty;
+pub mod number_format;
+pub mod types;
+pub mod worksheet;
+
+mod functions;
+
+mod actions;
+mod cast;
+mod constants;
+mod styles;
+
+mod diffs;
+mod implicit_intersection;
+
+mod units;
+mod utils;
+mod workbook;
+
+#[cfg(test)]
+mod test;
+
+#[cfg(test)]
+pub mod mock_time;
+
\ No newline at end of file diff --git a/src/ironcalc_base/locale/mod.rs.html b/src/ironcalc_base/locale/mod.rs.html new file mode 100644 index 0000000..bfe1299 --- /dev/null +++ b/src/ironcalc_base/locale/mod.rs.html @@ -0,0 +1,187 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+
use once_cell::sync::Lazy;
+use serde::{Deserialize, Serialize};
+
+use std::collections::HashMap;
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Locale {
+    pub dates: Dates,
+    pub numbers: NumbersProperties,
+    pub currency: Currency,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Currency {
+    pub iso: String,
+    pub symbol: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct NumbersProperties {
+    #[serde(rename = "symbols-numberSystem-latn")]
+    pub symbols: NumbersSymbols,
+    #[serde(rename = "decimalFormats-numberSystem-latn")]
+    pub decimal_formats: DecimalFormats,
+    #[serde(rename = "currencyFormats-numberSystem-latn")]
+    pub currency_formats: CurrencyFormats,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Dates {
+    pub day_names: Vec<String>,
+    pub day_names_short: Vec<String>,
+    pub months: Vec<String>,
+    pub months_short: Vec<String>,
+    pub months_letter: Vec<String>,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct NumbersSymbols {
+    pub decimal: String,
+    pub group: String,
+    pub list: String,
+    pub percent_sign: String,
+    pub plus_sign: String,
+    pub minus_sign: String,
+    pub approximately_sign: String,
+    pub exponential: String,
+    pub superscripting_exponent: String,
+    pub per_mille: String,
+    pub infinity: String,
+    pub nan: String,
+    pub time_separator: String,
+}
+
+// See: https://cldr.unicode.org/translation/number-currency-formats/number-and-currency-patterns
+#[derive(Serialize, Deserialize, Clone)]
+pub struct CurrencyFormats {
+    pub standard: String,
+    #[serde(rename = "standard-alphaNextToNumber")]
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub standard_alpha_next_to_number: Option<String>,
+    #[serde(rename = "standard-noCurrency")]
+    pub standard_no_currency: String,
+    pub accounting: String,
+    #[serde(rename = "accounting-alphaNextToNumber")]
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub accounting_alpha_next_to_number: Option<String>,
+    #[serde(rename = "accounting-noCurrency")]
+    pub accounting_no_currency: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct DecimalFormats {
+    pub standard: String,
+}
+
+static LOCALES: Lazy<HashMap<String, Locale>> = Lazy::new(|| {
+    serde_json::from_str(include_str!("locales.json")).expect("Failed parsing locale")
+});
+
+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)
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/model.rs.html b/src/ironcalc_base/model.rs.html new file mode 100644 index 0000000..a499c13 --- /dev/null +++ b/src/ironcalc_base/model.rs.html @@ -0,0 +1,3147 @@ +model.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
+1288
+1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
+1301
+1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
+1316
+1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
+1331
+1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
+1344
+1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
+1355
+1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
+1364
+1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
+1385
+1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
+1406
+1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
+1416
+1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
+1437
+1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
+1458
+1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
+1485
+1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
+1498
+1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
+1529
+1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
+1560
+1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+
use serde::{Deserialize, Serialize};
+use serde_json::json;
+
+use std::collections::HashMap;
+use std::vec::Vec;
+
+use crate::{
+    calc_result::{CalcResult, CellReference, Range},
+    cell::CellValue,
+    constants::{self, LAST_COLUMN, LAST_ROW},
+    expressions::token::{Error, OpCompare, OpProduct, OpSum, OpUnary},
+    expressions::{
+        parser::move_formula::{move_formula, MoveContext},
+        token::get_error_by_name,
+        types::*,
+        utils::{self, is_valid_row},
+    },
+    expressions::{
+        parser::{
+            stringify::{to_rc_format, to_string},
+            Node, Parser,
+        },
+        utils::is_valid_column_number,
+    },
+    formatter::{
+        format::{format_number, parse_formatted_number},
+        lexer::is_likely_date_number_format,
+    },
+    functions::util::compare_values,
+    implicit_intersection::implicit_intersection,
+    language::{get_language, Language},
+    locale::{get_locale, Currency, Locale},
+    types::*,
+    utils as common,
+};
+
+pub use chrono_tz::Tz;
+
+#[cfg(test)]
+pub use crate::mock_time::get_milliseconds_since_epoch;
+
+#[cfg(not(test))]
+#[cfg(not(target_arch = "wasm32"))]
+pub fn get_milliseconds_since_epoch() -> i64 {
+    use std::time::{SystemTime, UNIX_EPOCH};
+    SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .expect("problem with system time")
+        .as_millis() as i64
+}
+
+#[cfg(not(test))]
+#[cfg(target_arch = "wasm32")]
+pub fn get_milliseconds_since_epoch() -> i64 {
+    use js_sys::Date;
+    Date::now() as i64
+}
+
+#[derive(Clone)]
+pub enum CellState {
+    Evaluated,
+    Evaluating,
+}
+
+#[derive(Debug, Clone)]
+pub enum ParsedDefinedName {
+    CellReference(CellReference),
+    RangeReference(Range),
+    InvalidDefinedNameFormula,
+    // TODO: Support constants in defined names
+    // TODO: Support formulas in defined names
+    // TODO: Support tables in defined names
+}
+
+/// A model includes:
+///     * A Workbook: An internal representation of and Excel workbook
+///     * Parsed Formulas: All the formulas in the workbook are parsed here (runtime only)
+///     * A list of cells with its status (evaluating, evaluated, not evaluated)
+///     * A dictionary with the shared strings and their indices.
+///       This is an optimization for large files (~1 million rows)
+#[derive(Clone)]
+pub struct Model {
+    pub workbook: Workbook,
+    pub parsed_formulas: Vec<Vec<Node>>,
+    pub parsed_defined_names: HashMap<(Option<u32>, String), ParsedDefinedName>,
+    pub shared_strings: HashMap<String, usize>,
+    pub parser: Parser,
+    pub cells: HashMap<(u32, i32, i32), CellState>,
+    pub locale: Locale,
+    pub language: Language,
+    pub tz: Tz,
+}
+
+pub struct CellIndex {
+    pub index: u32,
+    pub row: i32,
+    pub column: i32,
+}
+
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
+pub struct Style {
+    pub alignment: Option<Alignment>,
+    pub num_fmt: String,
+    pub fill: Fill,
+    pub font: Font,
+    pub border: Border,
+    pub quote_prefix: bool,
+}
+
+impl Model {
+    pub(crate) fn evaluate_node_with_reference(
+        &mut self,
+        node: &Node,
+        cell: CellReference,
+    ) -> CalcResult {
+        match node {
+            Node::ReferenceKind {
+                sheet_name: _,
+                sheet_index,
+                absolute_row,
+                absolute_column,
+                row,
+                column,
+            } => {
+                let mut row1 = *row;
+                let mut column1 = *column;
+                if !absolute_row {
+                    row1 += cell.row;
+                }
+                if !absolute_column {
+                    column1 += cell.column;
+                }
+                CalcResult::Range {
+                    left: CellReference {
+                        sheet: *sheet_index,
+                        row: row1,
+                        column: column1,
+                    },
+                    right: CellReference {
+                        sheet: *sheet_index,
+                        row: row1,
+                        column: column1,
+                    },
+                }
+            }
+            Node::RangeKind {
+                sheet_name: _,
+                sheet_index,
+                absolute_row1,
+                absolute_column1,
+                row1,
+                column1,
+                absolute_row2,
+                absolute_column2,
+                row2,
+                column2,
+            } => {
+                let mut row_left = *row1;
+                let mut column_left = *column1;
+                if !absolute_row1 {
+                    row_left += cell.row;
+                }
+                if !absolute_column1 {
+                    column_left += cell.column;
+                }
+                let mut row_right = *row2;
+                let mut column_right = *column2;
+                if !absolute_row2 {
+                    row_right += cell.row;
+                }
+                if !absolute_column2 {
+                    column_right += cell.column;
+                }
+                // FIXME: HACK. The parser is currently parsing Sheet3!A1:A10 as Sheet3!A1:(present sheet)!A10
+                CalcResult::Range {
+                    left: CellReference {
+                        sheet: *sheet_index,
+                        row: row_left,
+                        column: column_left,
+                    },
+                    right: CellReference {
+                        sheet: *sheet_index,
+                        row: row_right,
+                        column: column_right,
+                    },
+                }
+            }
+            _ => self.evaluate_node_in_context(node, cell),
+        }
+    }
+
+    fn get_range(&mut self, left: &Node, right: &Node, cell: CellReference) -> CalcResult {
+        let left_result = self.evaluate_node_with_reference(left, cell);
+        let right_result = self.evaluate_node_with_reference(right, cell);
+        match (left_result, right_result) {
+            (
+                CalcResult::Range {
+                    left: left1,
+                    right: right1,
+                },
+                CalcResult::Range {
+                    left: left2,
+                    right: right2,
+                },
+            ) => {
+                if left1.row == right1.row
+                    && left1.column == right1.column
+                    && left2.row == right2.row
+                    && left2.column == right2.column
+                {
+                    return CalcResult::Range {
+                        left: left1,
+                        right: right2,
+                    };
+                }
+                CalcResult::Error {
+                    error: Error::VALUE,
+                    origin: cell,
+                    message: "Invalid range".to_string(),
+                }
+            }
+            _ => CalcResult::Error {
+                error: Error::VALUE,
+                origin: cell,
+                message: "Invalid range".to_string(),
+            },
+        }
+    }
+
+    pub(crate) fn evaluate_node_in_context(
+        &mut self,
+        node: &Node,
+        cell: CellReference,
+    ) -> CalcResult {
+        use Node::*;
+        match node {
+            OpSumKind { kind, left, right } => {
+                // In the future once the feature try trait stabilizes we could use the '?' operator for this :)
+                // See: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=236044e8321a1450988e6ffe5a27dab5
+                let l = match self.get_number(left, cell) {
+                    Ok(f) => f,
+                    Err(s) => {
+                        return s;
+                    }
+                };
+                let r = match self.get_number(right, cell) {
+                    Ok(f) => f,
+                    Err(s) => {
+                        return s;
+                    }
+                };
+                let result = match kind {
+                    OpSum::Add => l + r,
+                    OpSum::Minus => l - r,
+                };
+                CalcResult::Number(result)
+            }
+            NumberKind(value) => CalcResult::Number(*value),
+            StringKind(value) => CalcResult::String(value.replace(r#""""#, r#"""#)),
+            BooleanKind(value) => CalcResult::Boolean(*value),
+            ReferenceKind {
+                sheet_name: _,
+                sheet_index,
+                absolute_row,
+                absolute_column,
+                row,
+                column,
+            } => {
+                let mut row1 = *row;
+                let mut column1 = *column;
+                if !absolute_row {
+                    row1 += cell.row;
+                }
+                if !absolute_column {
+                    column1 += cell.column;
+                }
+                self.evaluate_cell(CellReference {
+                    sheet: *sheet_index,
+                    row: row1,
+                    column: column1,
+                })
+            }
+            WrongReferenceKind { .. } => {
+                CalcResult::new_error(Error::REF, cell, "Wrong reference".to_string())
+            }
+            OpRangeKind { left, right } => self.get_range(left, right, cell),
+            WrongRangeKind { .. } => {
+                CalcResult::new_error(Error::REF, cell, "Wrong range".to_string())
+            }
+            RangeKind {
+                sheet_index,
+                row1,
+                column1,
+                row2,
+                column2,
+                absolute_column1,
+                absolute_row2,
+                absolute_row1,
+                absolute_column2,
+                sheet_name: _,
+            } => CalcResult::Range {
+                left: CellReference {
+                    sheet: *sheet_index,
+                    row: if *absolute_row1 {
+                        *row1
+                    } else {
+                        *row1 + cell.row
+                    },
+                    column: if *absolute_column1 {
+                        *column1
+                    } else {
+                        *column1 + cell.column
+                    },
+                },
+                right: CellReference {
+                    sheet: *sheet_index,
+                    row: if *absolute_row2 {
+                        *row2
+                    } else {
+                        *row2 + cell.row
+                    },
+                    column: if *absolute_column2 {
+                        *column2
+                    } else {
+                        *column2 + cell.column
+                    },
+                },
+            },
+            OpConcatenateKind { left, right } => {
+                let l = match self.get_string(left, cell) {
+                    Ok(f) => f,
+                    Err(s) => {
+                        return s;
+                    }
+                };
+                let r = match self.get_string(right, cell) {
+                    Ok(f) => f,
+                    Err(s) => {
+                        return s;
+                    }
+                };
+                let result = format!("{}{}", l, r);
+                CalcResult::String(result)
+            }
+            OpProductKind { kind, left, right } => {
+                let l = match self.get_number(left, cell) {
+                    Ok(f) => f,
+                    Err(s) => {
+                        return s;
+                    }
+                };
+                let r = match self.get_number(right, cell) {
+                    Ok(f) => f,
+                    Err(s) => {
+                        return s;
+                    }
+                };
+                let result = match kind {
+                    OpProduct::Times => l * r,
+                    OpProduct::Divide => {
+                        if r == 0.0 {
+                            return CalcResult::new_error(
+                                Error::DIV,
+                                cell,
+                                "Divide by Zero".to_string(),
+                            );
+                        }
+                        l / r
+                    }
+                };
+                CalcResult::Number(result)
+            }
+            OpPowerKind { left, right } => {
+                let l = match self.get_number(left, cell) {
+                    Ok(f) => f,
+                    Err(s) => {
+                        return s;
+                    }
+                };
+                let r = match self.get_number(right, cell) {
+                    Ok(f) => f,
+                    Err(s) => {
+                        return s;
+                    }
+                };
+                // Deal with errors properly
+                CalcResult::Number(l.powf(r))
+            }
+            FunctionKind { kind, args } => self.evaluate_function(kind, args, cell),
+            InvalidFunctionKind { name, args: _ } => {
+                CalcResult::new_error(Error::ERROR, cell, format!("Invalid function: {}", name))
+            }
+            ArrayKind(_) => {
+                // TODO: NOT IMPLEMENTED
+                CalcResult::new_error(Error::NIMPL, cell, "Arrays not implemented".to_string())
+            }
+            VariableKind(defined_name) => {
+                let parsed_defined_name = self
+                    .parsed_defined_names
+                    .get(&(Some(cell.sheet), defined_name.to_lowercase())) // try getting local defined name
+                    .or_else(|| {
+                        self.parsed_defined_names
+                            .get(&(None, defined_name.to_lowercase()))
+                    }); // fallback to global
+
+                if let Some(parsed_defined_name) = parsed_defined_name {
+                    match parsed_defined_name {
+                        ParsedDefinedName::CellReference(reference) => {
+                            self.evaluate_cell(*reference)
+                        }
+                        ParsedDefinedName::RangeReference(range) => CalcResult::Range {
+                            left: range.left,
+                            right: range.right,
+                        },
+                        ParsedDefinedName::InvalidDefinedNameFormula => CalcResult::new_error(
+                            Error::NIMPL,
+                            cell,
+                            format!("Defined name \"{}\" is not a reference.", defined_name),
+                        ),
+                    }
+                } else {
+                    CalcResult::new_error(
+                        Error::NAME,
+                        cell,
+                        format!("Defined name \"{}\" not found.", defined_name),
+                    )
+                }
+            }
+            CompareKind { kind, left, right } => {
+                let l = self.evaluate_node_in_context(left, cell);
+                if l.is_error() {
+                    return l;
+                }
+                let r = self.evaluate_node_in_context(right, cell);
+                if r.is_error() {
+                    return r;
+                }
+                let compare = compare_values(&l, &r);
+                match kind {
+                    OpCompare::Equal => {
+                        if compare == 0 {
+                            CalcResult::Boolean(true)
+                        } else {
+                            CalcResult::Boolean(false)
+                        }
+                    }
+                    OpCompare::LessThan => {
+                        if compare == -1 {
+                            CalcResult::Boolean(true)
+                        } else {
+                            CalcResult::Boolean(false)
+                        }
+                    }
+                    OpCompare::GreaterThan => {
+                        if compare == 1 {
+                            CalcResult::Boolean(true)
+                        } else {
+                            CalcResult::Boolean(false)
+                        }
+                    }
+                    OpCompare::LessOrEqualThan => {
+                        if compare < 1 {
+                            CalcResult::Boolean(true)
+                        } else {
+                            CalcResult::Boolean(false)
+                        }
+                    }
+                    OpCompare::GreaterOrEqualThan => {
+                        if compare > -1 {
+                            CalcResult::Boolean(true)
+                        } else {
+                            CalcResult::Boolean(false)
+                        }
+                    }
+                    OpCompare::NonEqual => {
+                        if compare != 0 {
+                            CalcResult::Boolean(true)
+                        } else {
+                            CalcResult::Boolean(false)
+                        }
+                    }
+                }
+            }
+            UnaryKind { kind, right } => {
+                let r = match self.get_number(right, cell) {
+                    Ok(f) => f,
+                    Err(s) => {
+                        return s;
+                    }
+                };
+                match kind {
+                    OpUnary::Minus => CalcResult::Number(-r),
+                    OpUnary::Percentage => CalcResult::Number(r / 100.0),
+                }
+            }
+            ErrorKind(kind) => CalcResult::new_error(kind.clone(), cell, "".to_string()),
+            ParseErrorKind {
+                formula,
+                message,
+                position: _,
+            } => CalcResult::new_error(
+                Error::ERROR,
+                cell,
+                format!("Error parsing {}: {}", formula, message),
+            ),
+            EmptyArgKind => CalcResult::EmptyArg,
+        }
+    }
+
+    fn cell_reference_to_string(&self, cell_reference: &CellReference) -> Result<String, String> {
+        let sheet = self.workbook.worksheet(cell_reference.sheet)?;
+        let column = utils::number_to_column(cell_reference.column)
+            .ok_or_else(|| "Invalid column".to_string())?;
+        if !is_valid_row(cell_reference.row) {
+            return Err("Invalid row".to_string());
+        }
+        Ok(format!("{}!{}{}", sheet.name, column, cell_reference.row))
+    }
+    /// Sets `result` in the cell given by `sheet` sheet index, row and column
+    /// Note that will panic if the cell does not exist
+    /// It will do nothing if the cell does not have a formula
+    fn set_cell_value(&mut self, cell_reference: CellReference, result: &CalcResult) {
+        let CellReference { sheet, column, row } = cell_reference;
+        let cell = &self.workbook.worksheets[sheet as usize].sheet_data[&row][&column];
+        let s = cell.get_style();
+        if let Some(f) = cell.get_formula() {
+            match result {
+                CalcResult::Number(value) => {
+                    // safety belt
+                    if value.is_nan() || value.is_infinite() {
+                        // This should never happen, is there a way we can log this events?
+                        return self.set_cell_value(
+                            cell_reference,
+                            &CalcResult::Error {
+                                error: Error::NUM,
+                                origin: cell_reference,
+                                message: "".to_string(),
+                            },
+                        );
+                    }
+                    *self.workbook.worksheets[sheet as usize]
+                        .sheet_data
+                        .get_mut(&row)
+                        .expect("expected a row")
+                        .get_mut(&column)
+                        .expect("expected a column") = Cell::CellFormulaNumber { f, s, v: *value };
+                }
+                CalcResult::String(value) => {
+                    *self.workbook.worksheets[sheet as usize]
+                        .sheet_data
+                        .get_mut(&row)
+                        .expect("expected a row")
+                        .get_mut(&column)
+                        .expect("expected a column") = Cell::CellFormulaString {
+                        f,
+                        s,
+                        v: value.clone(),
+                    };
+                }
+                CalcResult::Boolean(value) => {
+                    *self.workbook.worksheets[sheet as usize]
+                        .sheet_data
+                        .get_mut(&row)
+                        .expect("expected a row")
+                        .get_mut(&column)
+                        .expect("expected a column") = Cell::CellFormulaBoolean { f, s, v: *value };
+                }
+                CalcResult::Error {
+                    error,
+                    origin,
+                    message,
+                } => {
+                    let o = match self.cell_reference_to_string(origin) {
+                        Ok(s) => s,
+                        Err(_) => "".to_string(),
+                    };
+                    *self.workbook.worksheets[sheet as usize]
+                        .sheet_data
+                        .get_mut(&row)
+                        .expect("expected a row")
+                        .get_mut(&column)
+                        .expect("expected a column") = Cell::CellFormulaError {
+                        f,
+                        s,
+                        o,
+                        m: message.to_string(),
+                        ei: error.clone(),
+                    };
+                }
+                CalcResult::Range { left, right } => {
+                    let range = Range {
+                        left: *left,
+                        right: *right,
+                    };
+                    if let Some(intersection_cell) = implicit_intersection(&cell_reference, &range)
+                    {
+                        let v = self.evaluate_cell(intersection_cell);
+                        self.set_cell_value(cell_reference, &v);
+                    } else {
+                        let o = match self.cell_reference_to_string(&cell_reference) {
+                            Ok(s) => s,
+                            Err(_) => "".to_string(),
+                        };
+                        *self.workbook.worksheets[sheet as usize]
+                            .sheet_data
+                            .get_mut(&row)
+                            .expect("expected a row")
+                            .get_mut(&column)
+                            .expect("expected a column") = Cell::CellFormulaError {
+                            f,
+                            s,
+                            o,
+                            m: "Invalid reference".to_string(),
+                            ei: Error::VALUE,
+                        };
+                    }
+                }
+                CalcResult::EmptyCell | CalcResult::EmptyArg => {
+                    *self.workbook.worksheets[sheet as usize]
+                        .sheet_data
+                        .get_mut(&row)
+                        .expect("expected a row")
+                        .get_mut(&column)
+                        .expect("expected a column") = Cell::CellFormulaNumber { f, s, v: 0.0 };
+                }
+            }
+        }
+    }
+
+    pub fn set_sheet_color(&mut self, sheet: u32, color: &str) -> Result<(), String> {
+        let worksheet = self.workbook.worksheet_mut(sheet)?;
+        if color.is_empty() {
+            worksheet.color = None;
+            return Ok(());
+        } else if common::is_valid_hex_color(color) {
+            worksheet.color = Some(color.to_string());
+            return Ok(());
+        }
+        Err(format!("Invalid color: {}", color))
+    }
+
+    fn get_cell_value(&self, cell: &Cell, cell_reference: CellReference) -> CalcResult {
+        use Cell::*;
+        match cell {
+            EmptyCell { .. } => CalcResult::EmptyCell,
+            BooleanCell { v, .. } => CalcResult::Boolean(*v),
+            NumberCell { v, .. } => CalcResult::Number(*v),
+            ErrorCell { ei, .. } => {
+                let message = ei.to_localized_error_string(&self.language);
+                CalcResult::new_error(ei.clone(), cell_reference, message)
+            }
+            SharedString { si, .. } => {
+                if let Some(s) = self.workbook.shared_strings.get(*si as usize) {
+                    CalcResult::String(s.clone())
+                } else {
+                    let message = "Invalid shared string".to_string();
+                    CalcResult::new_error(Error::ERROR, cell_reference, message)
+                }
+            }
+            CellFormula { .. } => CalcResult::Error {
+                error: Error::ERROR,
+                origin: cell_reference,
+                message: "Unevaluated formula".to_string(),
+            },
+            CellFormulaBoolean { v, .. } => CalcResult::Boolean(*v),
+            CellFormulaNumber { v, .. } => CalcResult::Number(*v),
+            CellFormulaString { v, .. } => CalcResult::String(v.clone()),
+            CellFormulaError { ei, o, m, .. } => {
+                if let Some(cell_reference) = self.parse_reference(o) {
+                    CalcResult::new_error(ei.clone(), cell_reference, m.clone())
+                } else {
+                    CalcResult::Error {
+                        error: ei.clone(),
+                        origin: cell_reference,
+                        message: ei.to_localized_error_string(&self.language),
+                    }
+                }
+            }
+        }
+    }
+
+    /// Returns true if cell is completely empty.
+    /// Cell with formula that evaluates to empty string is not considered empty.
+    pub fn is_empty_cell(&self, sheet: u32, row: i32, column: i32) -> Result<bool, String> {
+        let worksheet = self.workbook.worksheet(sheet)?;
+        worksheet.is_empty_cell(row, column)
+    }
+
+    pub(crate) fn evaluate_cell(&mut self, cell_reference: CellReference) -> CalcResult {
+        let row_data = match self.workbook.worksheets[cell_reference.sheet as usize]
+            .sheet_data
+            .get(&cell_reference.row)
+        {
+            Some(r) => r,
+            None => return CalcResult::EmptyCell,
+        };
+        let cell = match row_data.get(&cell_reference.column) {
+            Some(c) => c,
+            None => {
+                return CalcResult::EmptyCell;
+            }
+        };
+
+        match cell.get_formula() {
+            Some(f) => {
+                let key = (
+                    cell_reference.sheet,
+                    cell_reference.row,
+                    cell_reference.column,
+                );
+                match self.cells.get(&key) {
+                    Some(CellState::Evaluating) => {
+                        return CalcResult::new_error(
+                            Error::CIRC,
+                            cell_reference,
+                            "Circular reference detected".to_string(),
+                        );
+                    }
+                    Some(CellState::Evaluated) => {
+                        return self.get_cell_value(cell, cell_reference);
+                    }
+                    _ => {
+                        // mark cell as being evaluated
+                        self.cells.insert(key, CellState::Evaluating);
+                    }
+                }
+                let node = &self.parsed_formulas[cell_reference.sheet as usize][f as usize].clone();
+                let result = self.evaluate_node_in_context(node, cell_reference);
+                self.set_cell_value(cell_reference, &result);
+                // mark cell as evaluated
+                self.cells.insert(key, CellState::Evaluated);
+                result
+            }
+            None => self.get_cell_value(cell, cell_reference),
+        }
+    }
+
+    pub(crate) fn get_sheet_index_by_name(&self, name: &str) -> Option<u32> {
+        let worksheets = &self.workbook.worksheets;
+        for (index, worksheet) in worksheets.iter().enumerate() {
+            if worksheet.get_name().to_uppercase() == name.to_uppercase() {
+                return Some(index as u32);
+            }
+        }
+        None
+    }
+
+    // Public API
+    /// Returns a model from a String representation of a workbook
+    pub fn from_json(s: &str) -> Result<Model, String> {
+        let workbook: Workbook =
+            serde_json::from_str(s).map_err(|_| "Error parsing workbook".to_string())?;
+        Model::from_workbook(workbook)
+    }
+
+    pub fn from_workbook(workbook: Workbook) -> Result<Model, String> {
+        let parsed_formulas = Vec::new();
+        let worksheets = &workbook.worksheets;
+
+        let worksheet_names = worksheets.iter().map(|s| s.get_name()).collect();
+
+        // add all tables
+        // let mut tables = Vec::new();
+        // for worksheet in worksheets {
+        //     let mut tables_in_sheet = HashMap::new();
+        //     for table in &worksheet.tables {
+        //         tables_in_sheet.insert(table.name.clone(), table.clone());
+        //     }
+        //     tables.push(tables_in_sheet);
+        // }
+        let parser = Parser::new(worksheet_names, workbook.tables.clone());
+        let cells = HashMap::new();
+        let locale = get_locale(&workbook.settings.locale)
+            .map_err(|_| "Invalid locale".to_string())?
+            .clone();
+        let tz: Tz = workbook
+            .settings
+            .tz
+            .parse()
+            .map_err(|_| format!("Invalid timezone: {}", workbook.settings.tz))?;
+
+        // FIXME: Add support for display languages
+        let language = get_language("en").expect("").clone();
+        let mut shared_strings = HashMap::new();
+        for (index, s) in workbook.shared_strings.iter().enumerate() {
+            shared_strings.insert(s.to_string(), index);
+        }
+
+        let mut model = Model {
+            workbook,
+            parsed_formulas,
+            shared_strings,
+            parsed_defined_names: HashMap::new(),
+            parser,
+            cells,
+            language,
+            locale,
+            tz,
+        };
+
+        model.parse_formulas();
+        model.parse_defined_names();
+
+        Ok(model)
+    }
+
+    /// Parses a reference like "Sheet1!B4" into {0, 2, 4}
+    pub fn parse_reference(&self, s: &str) -> Option<CellReference> {
+        let bytes = s.as_bytes();
+        let mut sheet_name = "".to_string();
+        let mut column = "".to_string();
+        let mut row = "".to_string();
+        let mut state = "sheet"; // "sheet", "col", "row"
+        for &byte in bytes {
+            match state {
+                "sheet" => {
+                    if byte == b'!' {
+                        state = "col"
+                    } else {
+                        sheet_name.push(byte as char);
+                    }
+                }
+                "col" => {
+                    if byte.is_ascii_alphabetic() {
+                        column.push(byte as char);
+                    } else {
+                        state = "row";
+                        row.push(byte as char);
+                    }
+                }
+                _ => {
+                    row.push(byte as char);
+                }
+            }
+        }
+        let sheet = match self.get_sheet_index_by_name(&sheet_name) {
+            Some(s) => s,
+            None => return None,
+        };
+        let row = match row.parse::<i32>() {
+            Ok(r) => r,
+            Err(_) => return None,
+        };
+        if !(1..=constants::LAST_ROW).contains(&row) {
+            return None;
+        }
+
+        let column = match utils::column_to_number(&column) {
+            Ok(column) => {
+                if is_valid_column_number(column) {
+                    column
+                } else {
+                    return None;
+                }
+            }
+            Err(_) => return None,
+        };
+
+        Some(CellReference { sheet, row, column })
+    }
+
+    /// moves the value in area from source to target.
+    pub fn move_cell_value_to_area(
+        &mut self,
+        value: &str,
+        source: &CellReferenceIndex,
+        target: &CellReferenceIndex,
+        area: &Area,
+    ) -> Result<String, String> {
+        let source_sheet_name = self
+            .workbook
+            .worksheet(source.sheet)
+            .map_err(|e| format!("Could not find source worksheet: {}", e))?
+            .get_name();
+        if source.sheet != area.sheet {
+            return Err("Source and area are in different sheets".to_string());
+        }
+        if source.row < area.row || source.row >= area.row + area.height {
+            return Err("Source is outside the area".to_string());
+        }
+        if source.column < area.column || source.column >= area.column + area.width {
+            return Err("Source is outside the area".to_string());
+        }
+        let target_sheet_name = self
+            .workbook
+            .worksheet(target.sheet)
+            .map_err(|e| format!("Could not find target worksheet: {}", e))?
+            .get_name();
+        if let Some(formula) = value.strip_prefix('=') {
+            let cell_reference = CellReferenceRC {
+                sheet: source_sheet_name.to_owned(),
+                row: source.row,
+                column: source.column,
+            };
+            let formula_str = move_formula(
+                &self.parser.parse(formula, &Some(cell_reference)),
+                &MoveContext {
+                    source_sheet_name: &source_sheet_name,
+                    row: source.row,
+                    column: source.column,
+                    area,
+                    target_sheet_name: &target_sheet_name,
+                    row_delta: target.row - source.row,
+                    column_delta: target.column - source.column,
+                },
+            );
+            Ok(format!("={}", formula_str))
+        } else {
+            Ok(value.to_string())
+        }
+    }
+
+    /// 'Extends' the value from cell [sheet, row, column] to [target_row, target_column]
+    pub fn extend_to(
+        &self,
+        sheet: u32,
+        row: i32,
+        column: i32,
+        target_row: i32,
+        target_column: i32,
+    ) -> Result<String, String> {
+        let cell = self.workbook.worksheet(sheet)?.cell(row, column);
+        let result = match cell {
+            Some(cell) => match cell.get_formula() {
+                None => cell.get_text(&self.workbook.shared_strings, &self.language),
+                Some(i) => {
+                    let formula = &self.parsed_formulas[sheet as usize][i as usize];
+                    let cell_ref = CellReferenceRC {
+                        sheet: self.workbook.worksheets[sheet as usize].get_name(),
+                        row: target_row,
+                        column: target_column,
+                    };
+                    format!("={}", to_string(formula, &cell_ref))
+                }
+            },
+            None => "".to_string(),
+        };
+        Ok(result)
+    }
+
+    /// 'Extends' value from cell [sheet, row, column] to [target_row, target_column]
+    pub fn extend_copied_value(
+        &mut self, // FIXME: weird that it must be mutable
+        value: &str,
+        source_sheet_name: &str,
+        source: &CellReferenceIndex,
+        target: &CellReferenceIndex,
+    ) -> Result<String, String> {
+        let target_sheet_name = match self.workbook.worksheets.get(target.sheet as usize) {
+            Some(ws) => ws.get_name(),
+            None => {
+                return Err("Invalid worksheet index".to_owned());
+            }
+        };
+        if let Some(formula_str) = value.strip_prefix('=') {
+            let cell_reference = CellReferenceRC {
+                sheet: source_sheet_name.to_string(),
+                row: source.row,
+                column: source.column,
+            };
+            let formula = &self.parser.parse(formula_str, &Some(cell_reference));
+            let cell_reference = CellReferenceRC {
+                sheet: target_sheet_name,
+                row: target.row,
+                column: target.column,
+            };
+            return Ok(format!("={}", to_string(formula, &cell_reference)));
+        };
+        Ok(value.to_string())
+    }
+
+    pub fn cell_formula(
+        &self,
+        sheet: u32,
+        row: i32,
+        column: i32,
+    ) -> Result<Option<String>, String> {
+        let worksheet = self.workbook.worksheet(sheet)?;
+        Ok(worksheet.cell(row, column).and_then(|cell| {
+            cell.get_formula().map(|formula_index| {
+                let formula = &self.parsed_formulas[sheet as usize][formula_index as usize];
+                let cell_ref = CellReferenceRC {
+                    sheet: worksheet.get_name(),
+                    row,
+                    column,
+                };
+                format!("={}", to_string(formula, &cell_ref))
+            })
+        }))
+    }
+
+    /// Updates the value of a cell with some text
+    /// It does not change the style unless needs to add "quoting"
+    pub fn update_cell_with_text(&mut self, sheet: u32, row: i32, column: i32, value: &str) {
+        let style_index = self.get_cell_style_index(sheet, row, column);
+        let new_style_index;
+        if common::value_needs_quoting(value, &self.language) {
+            new_style_index = self
+                .workbook
+                .styles
+                .get_style_with_quote_prefix(style_index);
+        } else if self.workbook.styles.style_is_quote_prefix(style_index) {
+            new_style_index = self
+                .workbook
+                .styles
+                .get_style_without_quote_prefix(style_index);
+        } else {
+            new_style_index = style_index;
+        }
+        self.set_cell_with_string(sheet, row, column, value, new_style_index);
+    }
+
+    /// Updates the value of a cell with a boolean value
+    /// It does not change the style
+    pub fn update_cell_with_bool(&mut self, sheet: u32, row: i32, column: i32, value: bool) {
+        let style_index = self.get_cell_style_index(sheet, row, column);
+        let new_style_index = if self.workbook.styles.style_is_quote_prefix(style_index) {
+            self.workbook
+                .styles
+                .get_style_without_quote_prefix(style_index)
+        } else {
+            style_index
+        };
+        let worksheet = &mut self.workbook.worksheets[sheet as usize];
+        worksheet.set_cell_with_boolean(row, column, value, new_style_index);
+    }
+
+    /// Updates the value of a cell with a number
+    /// It does not change the style
+    pub fn update_cell_with_number(&mut self, sheet: u32, row: i32, column: i32, value: f64) {
+        let style_index = self.get_cell_style_index(sheet, row, column);
+        let new_style_index = if self.workbook.styles.style_is_quote_prefix(style_index) {
+            self.workbook
+                .styles
+                .get_style_without_quote_prefix(style_index)
+        } else {
+            style_index
+        };
+        let worksheet = &mut self.workbook.worksheets[sheet as usize];
+        worksheet.set_cell_with_number(row, column, value, new_style_index);
+    }
+
+    /// Updates the formula of given cell
+    /// It does not change the style unless needs to add "quoting"
+    /// Expects the formula to start with "="
+    pub fn update_cell_with_formula(
+        &mut self,
+        sheet: u32,
+        row: i32,
+        column: i32,
+        formula: String,
+    ) -> Result<(), String> {
+        let mut style_index = self.get_cell_style_index(sheet, row, column);
+        if self.workbook.styles.style_is_quote_prefix(style_index) {
+            style_index = self
+                .workbook
+                .styles
+                .get_style_without_quote_prefix(style_index);
+        }
+        let formula = formula
+            .strip_prefix('=')
+            .ok_or_else(|| format!("\"{formula}\" is not a valid formula"))?;
+        self.set_cell_with_formula(sheet, row, column, formula, style_index)?;
+        Ok(())
+    }
+
+    /// Sets a cell parametrized by (`sheet`, `row`, `column`) with `value`
+    /// This mimics a user entering a value on a cell.
+    /// If you enter a currency `$100` it will set as a number and update the style
+    /// Note that for currencies/percentage there is only one possible style
+    /// The value is always a string, so we need to try to cast it into numbers/booleans/errors
+    pub fn set_user_input(&mut self, sheet: u32, row: i32, column: i32, value: String) {
+        // If value starts with "'" then we force the style to be quote_prefix
+        let style_index = self.get_cell_style_index(sheet, row, column);
+        if let Some(new_value) = value.strip_prefix('\'') {
+            // First check if it needs quoting
+            let new_style = if common::value_needs_quoting(new_value, &self.language) {
+                self.workbook
+                    .styles
+                    .get_style_with_quote_prefix(style_index)
+            } else {
+                style_index
+            };
+            self.set_cell_with_string(sheet, row, column, new_value, new_style);
+        } else {
+            let mut new_style_index = style_index;
+            if self.workbook.styles.style_is_quote_prefix(style_index) {
+                new_style_index = self
+                    .workbook
+                    .styles
+                    .get_style_without_quote_prefix(style_index);
+            }
+            if let Some(formula) = value.strip_prefix('=') {
+                let formula_index = self
+                    .set_cell_with_formula(sheet, row, column, formula, new_style_index)
+                    .expect("could not set the cell formula");
+                // Update the style if needed
+                let cell = CellReference { sheet, row, column };
+                let parsed_formula = &self.parsed_formulas[sheet as usize][formula_index as usize];
+                if let Some(units) = self.compute_node_units(parsed_formula, &cell) {
+                    let new_style_index = self
+                        .workbook
+                        .styles
+                        .get_style_with_format(new_style_index, &units.get_num_fmt());
+                    let style = self.workbook.styles.get_style(new_style_index);
+                    self.set_cell_style(sheet, row, column, &style)
+                        .expect("Failed setting the style");
+                }
+            } else {
+                let worksheets = &mut self.workbook.worksheets;
+                let worksheet = &mut worksheets[sheet as usize];
+
+                // The list of currencies is '$', '€' and the local currency
+                let mut currencies = vec!["$", "€"];
+                let currency = &self.locale.currency.symbol;
+                if !currencies.iter().any(|e| e == currency) {
+                    currencies.push(currency);
+                }
+                //  We try to parse as number
+                if let Ok((v, number_format)) = parse_formatted_number(&value, &currencies) {
+                    if let Some(num_fmt) = number_format {
+                        // Should not apply the format in the following cases:
+                        // - we assign a date to already date-formatted cell
+                        let should_apply_format = !(is_likely_date_number_format(
+                            &self.workbook.styles.get_style(new_style_index).num_fmt,
+                        ) && is_likely_date_number_format(&num_fmt));
+                        if should_apply_format {
+                            new_style_index = self
+                                .workbook
+                                .styles
+                                .get_style_with_format(new_style_index, &num_fmt);
+                        }
+                    }
+                    worksheet.set_cell_with_number(row, column, v, new_style_index);
+                    return;
+                }
+                // We try to parse as boolean
+                if let Ok(v) = value.to_lowercase().parse::<bool>() {
+                    worksheet.set_cell_with_boolean(row, column, v, new_style_index);
+                    return;
+                }
+                // Check is it is error value
+                let upper = value.to_uppercase();
+                match get_error_by_name(&upper, &self.language) {
+                    Some(error) => {
+                        worksheet.set_cell_with_error(row, column, error, new_style_index);
+                    }
+                    None => {
+                        self.set_cell_with_string(sheet, row, column, &value, new_style_index);
+                    }
+                }
+            }
+        }
+    }
+
+    fn set_cell_with_formula(
+        &mut self,
+        sheet: u32,
+        row: i32,
+        column: i32,
+        formula: &str,
+        style: i32,
+    ) -> Result<i32, String> {
+        let worksheet = self.workbook.worksheet_mut(sheet)?;
+        let cell_reference = CellReferenceRC {
+            sheet: worksheet.get_name(),
+            row,
+            column,
+        };
+        let shared_formulas = &mut worksheet.shared_formulas;
+        let mut parsed_formula = self.parser.parse(formula, &Some(cell_reference.clone()));
+        // If the formula fails to parse try adding a parenthesis
+        // SUM(A1:A3  => SUM(A1:A3)
+        if let Node::ParseErrorKind { .. } = parsed_formula {
+            let new_parsed_formula = self
+                .parser
+                .parse(&format!("{})", formula), &Some(cell_reference));
+            match new_parsed_formula {
+                Node::ParseErrorKind { .. } => {}
+                _ => parsed_formula = new_parsed_formula,
+            }
+        }
+
+        let s = to_rc_format(&parsed_formula);
+        let mut formula_index: i32 = -1;
+        if let Some(index) = shared_formulas.iter().position(|x| x == &s) {
+            formula_index = index as i32;
+        }
+        if formula_index == -1 {
+            shared_formulas.push(s);
+            self.parsed_formulas[sheet as usize].push(parsed_formula);
+            formula_index = (shared_formulas.len() as i32) - 1;
+        }
+        worksheet.set_cell_with_formula(row, column, formula_index, style);
+        Ok(formula_index)
+    }
+
+    fn set_cell_with_string(&mut self, sheet: u32, row: i32, column: i32, value: &str, style: i32) {
+        let worksheets = &mut self.workbook.worksheets;
+        let worksheet = &mut worksheets[sheet as usize];
+        match self.shared_strings.get(value) {
+            Some(string_index) => {
+                worksheet.set_cell_with_string(row, column, *string_index as i32, style);
+            }
+            None => {
+                let string_index = self.workbook.shared_strings.len();
+                self.workbook.shared_strings.push(value.to_string());
+                self.shared_strings.insert(value.to_string(), string_index);
+                worksheet.set_cell_with_string(row, column, string_index as i32, style);
+            }
+        }
+    }
+
+    /// Gets the Excel Value (Bool, Number, String) of a cell
+    pub fn get_cell_value_by_ref(&self, cell_ref: &str) -> Result<CellValue, String> {
+        let cell_reference = match self.parse_reference(cell_ref) {
+            Some(c) => c,
+            None => return Err(format!("Error parsing reference: '{cell_ref}'")),
+        };
+        let sheet_index = cell_reference.sheet;
+        let column = cell_reference.column;
+        let row = cell_reference.row;
+
+        self.get_cell_value_by_index(sheet_index, row, column)
+    }
+
+    pub fn get_cell_value_by_index(
+        &self,
+        sheet_index: u32,
+        row: i32,
+        column: i32,
+    ) -> Result<CellValue, String> {
+        let cell = self
+            .workbook
+            .worksheet(sheet_index)?
+            .cell(row, column)
+            .cloned()
+            .unwrap_or_default();
+        let cell_value = cell.value(&self.workbook.shared_strings, &self.language);
+        Ok(cell_value)
+    }
+
+    pub fn formatted_cell_value(
+        &self,
+        sheet_index: u32,
+        row: i32,
+        column: i32,
+    ) -> Result<String, String> {
+        let format = self.get_style_for_cell(sheet_index, row, column).num_fmt;
+        let cell = self
+            .workbook
+            .worksheet(sheet_index)?
+            .cell(row, column)
+            .cloned()
+            .unwrap_or_default();
+        let formatted_value =
+            cell.formatted_value(&self.workbook.shared_strings, &self.language, |value| {
+                format_number(value, &format, &self.locale).text
+            });
+        Ok(formatted_value)
+    }
+
+    /// Returns a string with the cell content. If there is a formula returns the formula
+    /// If the cell is empty returns the empty string
+    /// Raises an error if there is no worksheet
+    pub fn get_cell_content(&self, sheet: u32, row: i32, column: i32) -> Result<String, String> {
+        let worksheet = self.workbook.worksheet(sheet)?;
+        let cell = match worksheet.cell(row, column) {
+            Some(c) => c,
+            None => return Ok("".to_string()),
+        };
+        match cell.get_formula() {
+            Some(formula_index) => {
+                let formula = &self.parsed_formulas[sheet as usize][formula_index as usize];
+                let cell_ref = CellReferenceRC {
+                    sheet: worksheet.get_name(),
+                    row,
+                    column,
+                };
+                Ok(format!("={}", to_string(formula, &cell_ref)))
+            }
+            None => Ok(cell.get_text(&self.workbook.shared_strings, &self.language)),
+        }
+    }
+    /// Returns a list of all cells
+    pub fn get_all_cells(&self) -> Vec<CellIndex> {
+        let mut cells = Vec::new();
+        for (index, sheet) in self.workbook.worksheets.iter().enumerate() {
+            let mut sorted_rows: Vec<_> = sheet.sheet_data.keys().collect();
+            sorted_rows.sort_unstable();
+            for row in sorted_rows {
+                let row_data = &sheet.sheet_data[row];
+                let mut sorted_columns: Vec<_> = row_data.keys().collect();
+                sorted_columns.sort_unstable();
+                for column in sorted_columns {
+                    cells.push(CellIndex {
+                        index: index as u32,
+                        row: *row,
+                        column: *column,
+                    });
+                }
+            }
+        }
+        cells
+    }
+
+    /// Evaluates the model with a top-down recursive algorithm
+    pub fn evaluate(&mut self) {
+        // clear all computation artifacts
+        self.cells.clear();
+
+        let cells = self.get_all_cells();
+
+        for cell in cells {
+            self.evaluate_cell(CellReference {
+                sheet: cell.index,
+                row: cell.row,
+                column: cell.column,
+            });
+        }
+    }
+
+    /// Sets cell to empty. Can be used to delete value without affecting style.
+    pub fn set_cell_empty(&mut self, sheet: u32, row: i32, column: i32) -> Result<(), String> {
+        let worksheet = self.workbook.worksheet_mut(sheet)?;
+        worksheet.set_cell_empty(row, column);
+        Ok(())
+    }
+
+    /// Deletes a cell by removing it from worksheet data.
+    pub fn delete_cell(&mut self, sheet: u32, row: i32, column: i32) -> Result<(), String> {
+        let worksheet = self.workbook.worksheet_mut(sheet)?;
+
+        let sheet_data = &mut worksheet.sheet_data;
+        if let Some(row_data) = sheet_data.get_mut(&row) {
+            row_data.remove(&column);
+        }
+
+        Ok(())
+    }
+
+    pub fn get_cell_style_index(&self, sheet: u32, row: i32, column: i32) -> i32 {
+        // First check the cell, then row, the column
+        let cell = self
+            .workbook
+            .worksheet(sheet)
+            .expect("Invalid sheet")
+            .cell(row, column);
+        match cell {
+            Some(cell) => cell.get_style(),
+            None => {
+                let rows = &self.workbook.worksheets[sheet as usize].rows;
+                for r in rows {
+                    if r.r == row {
+                        if r.custom_format {
+                            return r.s;
+                        } else {
+                            break;
+                        }
+                    }
+                }
+                let cols = &self.workbook.worksheets[sheet as usize].cols;
+                for c in cols.iter() {
+                    let min = c.min;
+                    let max = c.max;
+                    if column >= min && column <= max {
+                        return c.style.unwrap_or(0);
+                    }
+                }
+                0
+            }
+        }
+    }
+
+    pub fn get_style_for_cell(&self, sheet: u32, row: i32, column: i32) -> Style {
+        self.workbook
+            .styles
+            .get_style(self.get_cell_style_index(sheet, row, column))
+    }
+
+    /// Returns a JSON string of the workbook
+    pub fn to_json_str(&self) -> String {
+        match serde_json::to_string(&self.workbook) {
+            Ok(s) => s,
+            Err(_) => {
+                // TODO, is this branch possible at all?
+                json!({"error": "Error stringifying workbook"}).to_string()
+            }
+        }
+    }
+
+    /// Returns markup representation of the given `sheet`.
+    pub fn sheet_markup(&self, sheet: u32) -> Result<String, String> {
+        let worksheet = self.workbook.worksheet(sheet)?;
+        let dimension = worksheet.dimension();
+
+        let mut rows = Vec::new();
+
+        for row in 1..(dimension.max_row + 1) {
+            let mut row_markup: Vec<String> = Vec::new();
+
+            for column in 1..(dimension.max_column + 1) {
+                let mut cell_markup = match self.cell_formula(sheet, row, column)? {
+                    Some(formula) => formula,
+                    None => self.formatted_cell_value(sheet, row, column)?,
+                };
+                let style = self.get_style_for_cell(sheet, row, column);
+                if style.font.b {
+                    cell_markup = format!("**{cell_markup}**")
+                }
+                row_markup.push(cell_markup);
+            }
+
+            rows.push(row_markup.join("|"));
+        }
+
+        Ok(rows.join("\n"))
+    }
+
+    pub fn set_currency(&mut self, iso: &str) -> Result<(), &str> {
+        // TODO: Add a full list
+        let symbol = if iso == "USD" {
+            "$"
+        } else if iso == "EUR" {
+            "€"
+        } else if iso == "GBP" {
+            "£"
+        } else if iso == "JPY" {
+            "¥"
+        } else {
+            return Err("Unsupported currency");
+        };
+        self.locale.currency = Currency {
+            symbol: symbol.to_string(),
+            iso: iso.to_string(),
+        };
+        Ok(())
+    }
+
+    pub fn get_frozen_rows(&self, sheet: u32) -> Result<i32, String> {
+        if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
+            Ok(worksheet.frozen_rows)
+        } else {
+            Err("Invalid sheet".to_string())
+        }
+    }
+
+    pub fn get_frozen_columns(&self, sheet: u32) -> Result<i32, String> {
+        if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
+            Ok(worksheet.frozen_columns)
+        } else {
+            Err("Invalid sheet".to_string())
+        }
+    }
+
+    pub fn set_frozen_rows(&mut self, sheet: u32, frozen_rows: i32) -> Result<(), String> {
+        if let Some(worksheet) = self.workbook.worksheets.get_mut(sheet as usize) {
+            if frozen_rows < 0 {
+                return Err("Frozen rows cannot be negative".to_string());
+            } else if frozen_rows >= LAST_ROW {
+                return Err("Too many rows".to_string());
+            }
+            worksheet.frozen_rows = frozen_rows;
+            Ok(())
+        } else {
+            Err("Invalid sheet".to_string())
+        }
+    }
+
+    pub fn set_frozen_columns(&mut self, sheet: u32, frozen_columns: i32) -> Result<(), String> {
+        if let Some(worksheet) = self.workbook.worksheets.get_mut(sheet as usize) {
+            if frozen_columns < 0 {
+                return Err("Frozen columns cannot be negative".to_string());
+            } else if frozen_columns >= LAST_COLUMN {
+                return Err("Too many columns".to_string());
+            }
+            worksheet.frozen_columns = frozen_columns;
+            Ok(())
+        } else {
+            Err("Invalid sheet".to_string())
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::test::util::new_empty_model;
+
+    #[test]
+    fn test_cell_reference_to_string() {
+        let model = new_empty_model();
+        let reference = CellReference {
+            sheet: 0,
+            row: 32,
+            column: 16,
+        };
+        assert_eq!(
+            model.cell_reference_to_string(&reference),
+            Ok("Sheet1!P32".to_string())
+        )
+    }
+
+    #[test]
+    fn test_cell_reference_to_string_invalid_worksheet() {
+        let model = new_empty_model();
+        let reference = CellReference {
+            sheet: 10,
+            row: 1,
+            column: 1,
+        };
+        assert_eq!(
+            model.cell_reference_to_string(&reference),
+            Err("Invalid sheet index".to_string())
+        )
+    }
+
+    #[test]
+    fn test_cell_reference_to_string_invalid_column() {
+        let model = new_empty_model();
+        let reference = CellReference {
+            sheet: 0,
+            row: 1,
+            column: 20_000,
+        };
+        assert_eq!(
+            model.cell_reference_to_string(&reference),
+            Err("Invalid column".to_string())
+        )
+    }
+
+    #[test]
+    fn test_cell_reference_to_string_invalid_row() {
+        let model = new_empty_model();
+        let reference = CellReference {
+            sheet: 0,
+            row: 2_000_000,
+            column: 1,
+        };
+        assert_eq!(
+            model.cell_reference_to_string(&reference),
+            Err("Invalid row".to_string())
+        )
+    }
+
+    #[test]
+    fn test_get_cell() {
+        let mut model = new_empty_model();
+        model._set("A1", "35");
+        model._set("A2", "");
+        let worksheet = model.workbook.worksheet(0).expect("Invalid sheet");
+
+        assert_eq!(
+            worksheet.cell(1, 1),
+            Some(&Cell::NumberCell { v: 35.0, s: 0 })
+        );
+
+        assert_eq!(
+            worksheet.cell(2, 1),
+            Some(&Cell::SharedString { si: 0, s: 0 })
+        );
+        assert_eq!(worksheet.cell(3, 1), None)
+    }
+
+    #[test]
+    fn test_get_cell_invalid_sheet() {
+        let model = new_empty_model();
+        assert_eq!(
+            model.workbook.worksheet(5),
+            Err("Invalid sheet index".to_string()),
+        )
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/new_empty.rs.html b/src/ironcalc_base/new_empty.rs.html new file mode 100644 index 0000000..f6660f6 --- /dev/null +++ b/src/ironcalc_base/new_empty.rs.html @@ -0,0 +1,793 @@ +new_empty.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+
use chrono::NaiveDateTime;
+
+use std::collections::HashMap;
+
+use crate::{
+    calc_result::Range,
+    expressions::{
+        lexer::LexerMode,
+        parser::stringify::{rename_sheet_in_node, to_rc_format},
+        parser::Parser,
+        types::CellReferenceRC,
+    },
+    language::get_language,
+    locale::get_locale,
+    model::{get_milliseconds_since_epoch, Model, ParsedDefinedName},
+    types::{Metadata, SheetState, Workbook, WorkbookSettings, Worksheet},
+    utils::ParsedReference,
+};
+
+pub use chrono_tz::Tz;
+
+pub const APPLICATION: &str = "IronCalc Sheets";
+pub const APP_VERSION: &str = "10.0000";
+pub const IRONCALC_USER: &str = "IronCalc User";
+
+/// Name cannot be blank, must be shorter than 31 characters.
+/// You can use all alphanumeric characters but not the following special characters:
+/// \ , / , * , ? , : , [ , ].
+fn is_valid_sheet_name(name: &str) -> bool {
+    let invalid = ['\\', '/', '*', '?', ':', '[', ']'];
+    return !name.is_empty() && name.chars().count() <= 31 && !name.contains(&invalid[..]);
+}
+
+impl Model {
+    /// Creates a new worksheet. Note that it does not check if the name or the sheet_id exists
+    fn new_empty_worksheet(name: &str, sheet_id: u32) -> Worksheet {
+        Worksheet {
+            cols: vec![],
+            rows: vec![],
+            comments: vec![],
+            dimension: "A1".to_string(),
+            merge_cells: vec![],
+            name: name.to_string(),
+            shared_formulas: vec![],
+            sheet_data: Default::default(),
+            sheet_id,
+            state: SheetState::Visible,
+            color: Default::default(),
+            frozen_columns: 0,
+            frozen_rows: 0,
+        }
+    }
+
+    pub fn get_new_sheet_id(&self) -> u32 {
+        let mut index = 1;
+        let worksheets = &self.workbook.worksheets;
+        for worksheet in worksheets {
+            index = index.max(worksheet.sheet_id);
+        }
+        index + 1
+    }
+
+    pub(crate) fn parse_formulas(&mut self) {
+        self.parser.set_lexer_mode(LexerMode::R1C1);
+        let worksheets = &self.workbook.worksheets;
+        for worksheet in worksheets {
+            let shared_formulas = &worksheet.shared_formulas;
+            let cell_reference = &Some(CellReferenceRC {
+                sheet: worksheet.get_name(),
+                row: 1,
+                column: 1,
+            });
+            let mut parse_formula = Vec::new();
+            for formula in shared_formulas {
+                let t = self.parser.parse(formula, cell_reference);
+                parse_formula.push(t);
+            }
+            self.parsed_formulas.push(parse_formula);
+        }
+        self.parser.set_lexer_mode(LexerMode::A1);
+    }
+
+    pub(crate) fn parse_defined_names(&mut self) {
+        let mut parsed_defined_names = HashMap::new();
+        for defined_name in &self.workbook.defined_names {
+            let parsed_defined_name_formula = if let Ok(reference) =
+                ParsedReference::parse_reference_formula(
+                    None,
+                    &defined_name.formula,
+                    &self.locale,
+                    |name| self.get_sheet_index_by_name(name),
+                ) {
+                match reference {
+                    ParsedReference::CellReference(cell_reference) => {
+                        ParsedDefinedName::CellReference(cell_reference)
+                    }
+                    ParsedReference::Range(left, right) => {
+                        ParsedDefinedName::RangeReference(Range { left, right })
+                    }
+                }
+            } else {
+                ParsedDefinedName::InvalidDefinedNameFormula
+            };
+
+            let local_sheet_index = if let Some(sheet_id) = defined_name.sheet_id {
+                if let Some(sheet_index) = self.get_sheet_index_by_sheet_id(sheet_id) {
+                    Some(sheet_index)
+                } else {
+                    // TODO: Error: Sheet with given sheet_id not found.
+                    continue;
+                }
+            } else {
+                None
+            };
+
+            parsed_defined_names.insert(
+                (local_sheet_index, defined_name.name.to_lowercase()),
+                parsed_defined_name_formula,
+            );
+        }
+
+        self.parsed_defined_names = parsed_defined_names;
+    }
+
+    // Reparses all formulas and defined names
+    fn reset_parsed_structures(&mut self) {
+        self.parser
+            .set_worksheets(self.workbook.get_worksheet_names());
+        self.parsed_formulas = vec![];
+        self.parse_formulas();
+        self.parsed_defined_names = HashMap::new();
+        self.parse_defined_names();
+        self.evaluate();
+    }
+
+    /// Adds a sheet with a automatically generated name
+    pub fn new_sheet(&mut self) {
+        // First we find a name
+
+        // TODO: When/if we support i18n the name could depend on the locale
+        let base_name = "Sheet";
+        let base_name_uppercase = base_name.to_uppercase();
+        let mut index = 1;
+        while self
+            .workbook
+            .get_worksheet_names()
+            .iter()
+            .map(|s| s.to_uppercase())
+            .any(|x| x == format!("{}{}", base_name_uppercase, index))
+        {
+            index += 1;
+        }
+        let sheet_name = format!("{}{}", base_name, index);
+        // Now we need a sheet_id
+        let sheet_id = self.get_new_sheet_id();
+        let worksheet = Model::new_empty_worksheet(&sheet_name, sheet_id);
+        self.workbook.worksheets.push(worksheet);
+        self.reset_parsed_structures();
+    }
+
+    /// Inserts a sheet with a particular index
+    /// Fails if a worksheet with that name already exists or the name is invalid
+    /// Fails if the index is too large
+    pub fn insert_sheet(
+        &mut self,
+        sheet_name: &str,
+        sheet_index: u32,
+        sheet_id: Option<u32>,
+    ) -> Result<(), String> {
+        if !is_valid_sheet_name(sheet_name) {
+            return Err(format!("Invalid name for a sheet: '{}'", sheet_name));
+        }
+        if self
+            .workbook
+            .get_worksheet_names()
+            .iter()
+            .map(|s| s.to_uppercase())
+            .any(|x| x == sheet_name.to_uppercase())
+        {
+            return Err("A worksheet already exists with that name".to_string());
+        }
+        let sheet_id = match sheet_id {
+            Some(id) => id,
+            None => self.get_new_sheet_id(),
+        };
+        let worksheet = Model::new_empty_worksheet(sheet_name, sheet_id);
+        if sheet_index as usize > self.workbook.worksheets.len() {
+            return Err("Sheet index out of range".to_string());
+        }
+        self.workbook
+            .worksheets
+            .insert(sheet_index as usize, worksheet);
+        self.reset_parsed_structures();
+        Ok(())
+    }
+
+    /// Adds a sheet with a specific name
+    /// Fails if a worksheet with that name already exists or the name is invalid
+    pub fn add_sheet(&mut self, sheet_name: &str) -> Result<(), String> {
+        self.insert_sheet(sheet_name, self.workbook.worksheets.len() as u32, None)
+    }
+
+    /// Renames a sheet and updates all existing references to that sheet.
+    /// It can fail if:
+    ///   * The original sheet does not exists
+    ///   * The target sheet already exists
+    ///   * The target sheet name is invalid
+    pub fn rename_sheet(&mut self, old_name: &str, new_name: &str) -> Result<(), String> {
+        if let Some(sheet_index) = self.get_sheet_index_by_name(old_name) {
+            return self.rename_sheet_by_index(sheet_index, new_name);
+        }
+        Err(format!("Could not find sheet {}", old_name))
+    }
+
+    /// Renames a sheet and updates all existing references to that sheet.
+    /// It can fail if:
+    ///   * The original index is too large
+    ///   * The target sheet name already exists
+    ///   * The target sheet name is invalid
+    pub fn rename_sheet_by_index(
+        &mut self,
+        sheet_index: u32,
+        new_name: &str,
+    ) -> Result<(), String> {
+        if !is_valid_sheet_name(new_name) {
+            return Err(format!("Invalid name for a sheet: '{}'", new_name));
+        }
+        if self.get_sheet_index_by_name(new_name).is_some() {
+            return Err(format!("Sheet already exists: '{}'", new_name));
+        }
+        let worksheets = &self.workbook.worksheets;
+        let sheet_count = worksheets.len() as u32;
+        if sheet_index >= sheet_count {
+            return Err("Sheet index out of bounds".to_string());
+        }
+        // Parse all formulas with the old name
+        // All internal formulas are R1C1
+        self.parser.set_lexer_mode(LexerMode::R1C1);
+        // We use iter because the default would be a mut_iter and we don't need a mutable reference
+        let worksheets = &mut self.workbook.worksheets;
+        for worksheet in worksheets {
+            let cell_reference = &Some(CellReferenceRC {
+                sheet: worksheet.get_name(),
+                row: 1,
+                column: 1,
+            });
+            let mut formulas = Vec::new();
+            for formula in &worksheet.shared_formulas {
+                let mut t = self.parser.parse(formula, cell_reference);
+                rename_sheet_in_node(&mut t, sheet_index, new_name);
+                formulas.push(to_rc_format(&t));
+            }
+            worksheet.shared_formulas = formulas;
+        }
+        // Se the mode back to A1
+        self.parser.set_lexer_mode(LexerMode::A1);
+        // Update the name of the worksheet
+        let worksheets = &mut self.workbook.worksheets;
+        worksheets[sheet_index as usize].set_name(new_name);
+        self.reset_parsed_structures();
+        Ok(())
+    }
+
+    /// Deletes a sheet by index. Fails if:
+    ///   * The sheet does not exists
+    ///   * It is the last sheet
+    pub fn delete_sheet(&mut self, sheet_index: u32) -> Result<(), String> {
+        let worksheets = &self.workbook.worksheets;
+        let sheet_count = worksheets.len() as u32;
+        if sheet_count == 1 {
+            return Err("Cannot delete only sheet".to_string());
+        };
+        if sheet_index > sheet_count {
+            return Err("Sheet index too large".to_string());
+        }
+        self.workbook.worksheets.remove(sheet_index as usize);
+        self.reset_parsed_structures();
+        Ok(())
+    }
+
+    /// Deletes a sheet by name. Fails if:
+    ///   * The sheet does not exists
+    ///   * It is the last sheet
+    pub fn delete_sheet_by_name(&mut self, name: &str) -> Result<(), String> {
+        if let Some(sheet_index) = self.get_sheet_index_by_name(name) {
+            self.delete_sheet(sheet_index)
+        } else {
+            Err("Sheet not found".to_string())
+        }
+    }
+
+    /// Deletes a sheet by sheet_id. Fails if:
+    ///   * The sheet by sheet_id does not exists
+    ///   * It is the last sheet
+    pub fn delete_sheet_by_sheet_id(&mut self, sheet_id: u32) -> Result<(), String> {
+        if let Some(sheet_index) = self.get_sheet_index_by_sheet_id(sheet_id) {
+            self.delete_sheet(sheet_index)
+        } else {
+            Err("Sheet not found".to_string())
+        }
+    }
+
+    pub(crate) fn get_sheet_index_by_sheet_id(&self, sheet_id: u32) -> Option<u32> {
+        let worksheets = &self.workbook.worksheets;
+        for (index, worksheet) in worksheets.iter().enumerate() {
+            if worksheet.sheet_id == sheet_id {
+                return Some(index as u32);
+            }
+        }
+        None
+    }
+
+    /// Creates a new workbook with one empty sheet
+    pub fn new_empty(name: &str, locale_id: &str, timezone: &str) -> Result<Model, String> {
+        let tz: Tz = match &timezone.parse() {
+            Ok(tz) => *tz,
+            Err(_) => return Err(format!("Invalid timezone: {}", &timezone)),
+        };
+        let locale = match get_locale(locale_id) {
+            Ok(l) => l.clone(),
+            Err(_) => return Err(format!("Invalid locale: {}", locale_id)),
+        };
+
+        let milliseconds = get_milliseconds_since_epoch();
+        let seconds = milliseconds / 1000;
+        let dt = match NaiveDateTime::from_timestamp_opt(seconds, 0) {
+            Some(s) => s,
+            None => return Err(format!("Invalid timestamp: {}", milliseconds)),
+        };
+        // "2020-08-06T21:20:53Z
+        let now = dt.format("%Y-%m-%dT%H:%M:%SZ").to_string();
+
+        // String versions of the locale are added here to simplify the serialize/deserialize logic
+        let workbook = Workbook {
+            shared_strings: vec![],
+            defined_names: vec![],
+            worksheets: vec![Model::new_empty_worksheet("Sheet1", 1)],
+            styles: Default::default(),
+            name: name.to_string(),
+            settings: WorkbookSettings {
+                tz: timezone.to_string(),
+                locale: locale_id.to_string(),
+            },
+            metadata: Metadata {
+                application: APPLICATION.to_string(),
+                app_version: APP_VERSION.to_string(),
+                creator: IRONCALC_USER.to_string(),
+                last_modified_by: IRONCALC_USER.to_string(),
+                created: now.clone(),
+                last_modified: now,
+            },
+            tables: HashMap::new(),
+        };
+        let parsed_formulas = Vec::new();
+        let worksheets = &workbook.worksheets;
+        let worksheet_names = worksheets.iter().map(|s| s.get_name()).collect();
+        let parser = Parser::new(worksheet_names, HashMap::new());
+        let cells = HashMap::new();
+
+        // FIXME: Add support for display languages
+        let language = get_language("en").expect("").clone();
+
+        let mut model = Model {
+            workbook,
+            shared_strings: HashMap::new(),
+            parsed_formulas,
+            parsed_defined_names: HashMap::new(),
+            parser,
+            cells,
+            locale,
+            language,
+            tz,
+        };
+        model.parse_formulas();
+        Ok(model)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_is_valid_sheet_name() {
+        assert!(is_valid_sheet_name("Sheet1"));
+        assert!(is_valid_sheet_name("Zażółć gęślą jaźń"));
+
+        assert!(is_valid_sheet_name(" "));
+        assert!(!is_valid_sheet_name(""));
+
+        assert!(is_valid_sheet_name("🙈"));
+
+        assert!(is_valid_sheet_name("AAAAAAAAAABBBBBBBBBBCCCCCCCCCCD")); // 31
+        assert!(!is_valid_sheet_name("AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDE")); // 32
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/number_format.rs.html b/src/ironcalc_base/number_format.rs.html new file mode 100644 index 0000000..a0cb103 --- /dev/null +++ b/src/ironcalc_base/number_format.rs.html @@ -0,0 +1,315 @@ +number_format.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+
use crate::{
+    formatter::{self, format::Formatted},
+    locale::get_locale,
+    types::NumFmt,
+};
+
+const DEFAULT_NUM_FMTS: &[&str] = &[
+    "general",
+    "0",
+    "0.00",
+    "#,## 0",
+    "#,## 0.00",
+    "$#,## 0; \\ - $#,## 0",
+    "$#,## 0; [Red] \\ - $#,## 0",
+    "$#,## 0.00; \\ - $#,## 0.00",
+    "$#,## 0.00; [Red] \\ - $#,## 0.00",
+    "0%",
+    "0.00%",
+    "0.00E + 00",
+    "#?/?",
+    "#?? / ??",
+    "mm-dd-yy",
+    "d-mmm-yy",
+    "d-mmm",
+    "mmm-yy",
+    "h:mm AM / PM",
+    "h:mm:ss AM / PM",
+    "h:mm",
+    "h:mm:ss",
+    "m / d / yy h:mm",
+    "#,## 0;()#,## 0)",
+    "#,## 0; [Red]()#,## 0)",
+    "#,## 0.00;()#,## 0.00)",
+    "#,## 0.00; [Red]()#,## 0.00)",
+    "_()$”*#,## 0.00 _); _()$”* \\()#,## 0.00\\); _()$”* - ?? _); _()@_)",
+    "mm:ss",
+    "[h]:mm:ss",
+    "mmss .0",
+    "## 0.0E + 0",
+    "@",
+    "[$ -404] e / m / d ",
+    "m / d / yy",
+    "[$ -404] e / m / d",
+    "[$ -404] e / / d",
+    "[$ -404] e / m / d",
+    "t0",
+    "t0.00",
+    "t#,## 0",
+    "t#,## 0.00",
+    "t0%",
+    "t0.00 %",
+    "t#?/?",
+];
+
+pub fn get_default_num_fmt_id(num_fmt: &str) -> Option<i32> {
+    for (index, default_num_fmt) in DEFAULT_NUM_FMTS.iter().enumerate() {
+        if default_num_fmt == &num_fmt {
+            return Some(index as i32);
+        };
+    }
+    None
+}
+
+pub fn get_num_fmt(num_fmt_id: i32, num_fmts: &[NumFmt]) -> String {
+    // Check if it defined
+    for num_fmt in num_fmts {
+        if num_fmt.num_fmt_id == num_fmt_id {
+            return num_fmt.format_code.clone();
+        }
+    }
+    // Return one of the default ones
+    if num_fmt_id < DEFAULT_NUM_FMTS.len() as i32 {
+        return DEFAULT_NUM_FMTS[num_fmt_id as usize].to_string();
+    }
+    // Return general
+    DEFAULT_NUM_FMTS[0].to_string()
+}
+
+pub fn get_new_num_fmt_index(num_fmts: &[NumFmt]) -> i32 {
+    let mut index = DEFAULT_NUM_FMTS.len() as i32;
+    let mut found = true;
+    while found {
+        found = false;
+        for num_fmt in num_fmts {
+            if num_fmt.num_fmt_id == index {
+                found = true;
+                index += 1;
+                break;
+            }
+        }
+    }
+    index
+}
+
+pub fn to_precision(value: f64, precision: usize) -> f64 {
+    if value.is_infinite() || value.is_nan() {
+        return value;
+    }
+    to_precision_str(value, precision)
+        .parse::<f64>()
+        .unwrap_or({
+            // TODO: do this in a way that does not require a possible error
+            0.0
+        })
+}
+
+/// It rounds a `f64` with `p` significant figures:
+/// ```
+///     use ironcalc_base::number_format;
+///     assert_eq!(number_format::to_precision(0.1+0.2, 15), 0.3);
+///     assert_eq!(number_format::to_excel_precision_str(0.1+0.2), "0.3");
+/// ```
+/// This intends to be equivalent to the js: `${parseFloat(value.toPrecision(precision)})`
+/// See ([ecma](https://tc39.es/ecma262/#sec-number.prototype.toprecision)).
+/// FIXME: There has to be a better algorithm :/
+pub fn to_excel_precision_str(value: f64) -> String {
+    to_precision_str(value, 15)
+}
+pub fn to_precision_str(value: f64, precision: usize) -> String {
+    if value.is_infinite() {
+        return "inf".to_string();
+    }
+    if value.is_nan() {
+        return "NaN".to_string();
+    }
+    let exponent = value.abs().log10().floor();
+    let base = value / 10.0_f64.powf(exponent);
+    let base = format!("{0:.1$}", base, precision - 1);
+    let value = format!("{}e{}", base, exponent).parse::<f64>().unwrap_or({
+        // TODO: do this in a way that does not require a possible error
+        0.0
+    });
+    // I would love to use the std library. There is not a speed concern here
+    // problem is it doesn't do the right thing
+    // Also ryu is my favorite _modern_ algorithm
+    let mut buffer = ryu::Buffer::new();
+    let text = buffer.format(value);
+    // The above algorithm converts 2 to 2.0 regrettably
+    if let Some(stripped) = text.strip_suffix(".0") {
+        return stripped.to_string();
+    }
+    text.to_string()
+}
+
+pub fn format_number(value: f64, format_code: &str, locale: &str) -> Formatted {
+    let locale = match get_locale(locale) {
+        Ok(l) => l,
+        Err(_) => {
+            return Formatted {
+                text: "#ERROR!".to_owned(),
+                color: None,
+                error: Some("Invalid locale".to_string()),
+            }
+        }
+    };
+    formatter::format::format_number(value, format_code, locale)
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/styles.rs.html b/src/ironcalc_base/styles.rs.html new file mode 100644 index 0000000..1b86c38 --- /dev/null +++ b/src/ironcalc_base/styles.rs.html @@ -0,0 +1,583 @@ +styles.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+
use crate::{
+    model::{Model, Style},
+    number_format::{get_default_num_fmt_id, get_new_num_fmt_index, get_num_fmt},
+    types::{Border, CellStyles, CellXfs, Fill, Font, NumFmt, Styles},
+};
+
+// TODO: Move Styles and all related types from crate::types here
+// Not doing it right now to not have conflicts with exporter branch
+impl Styles {
+    fn get_font_index(&self, font: &Font) -> Option<i32> {
+        for (font_index, item) in self.fonts.iter().enumerate() {
+            if item == font {
+                return Some(font_index as i32);
+            }
+        }
+        None
+    }
+    fn get_fill_index(&self, fill: &Fill) -> Option<i32> {
+        for (fill_index, item) in self.fills.iter().enumerate() {
+            if item == fill {
+                return Some(fill_index as i32);
+            }
+        }
+        None
+    }
+    fn get_border_index(&self, border: &Border) -> Option<i32> {
+        for (border_index, item) in self.borders.iter().enumerate() {
+            if item == border {
+                return Some(border_index as i32);
+            }
+        }
+        None
+    }
+    fn get_num_fmt_index(&self, format_code: &str) -> Option<i32> {
+        if let Some(index) = get_default_num_fmt_id(format_code) {
+            return Some(index);
+        }
+        for item in self.num_fmts.iter() {
+            if item.format_code == format_code {
+                return Some(item.num_fmt_id);
+            }
+        }
+        None
+    }
+
+    pub fn create_new_style(&mut self, style: &Style) -> i32 {
+        let font = &style.font;
+        let font_id = if let Some(index) = self.get_font_index(font) {
+            index
+        } else {
+            self.fonts.push(font.clone());
+            self.fonts.len() as i32 - 1
+        };
+        let fill = &style.fill;
+        let fill_id = if let Some(index) = self.get_fill_index(fill) {
+            index
+        } else {
+            self.fills.push(fill.clone());
+            self.fills.len() as i32 - 1
+        };
+        let border = &style.border;
+        let border_id = if let Some(index) = self.get_border_index(border) {
+            index
+        } else {
+            self.borders.push(border.clone());
+            self.borders.len() as i32 - 1
+        };
+        let num_fmt = &style.num_fmt;
+        let num_fmt_id;
+        if let Some(index) = self.get_num_fmt_index(num_fmt) {
+            num_fmt_id = index;
+        } else {
+            num_fmt_id = get_new_num_fmt_index(&self.num_fmts);
+            self.num_fmts.push(NumFmt {
+                format_code: num_fmt.to_string(),
+                num_fmt_id,
+            });
+        }
+        self.cell_xfs.push(CellXfs {
+            xf_id: 0,
+            num_fmt_id,
+            font_id,
+            fill_id,
+            border_id,
+            apply_number_format: false,
+            apply_border: false,
+            apply_alignment: false,
+            apply_protection: false,
+            apply_font: false,
+            apply_fill: false,
+            quote_prefix: style.quote_prefix,
+            alignment: style.alignment.clone(),
+        });
+        self.cell_xfs.len() as i32 - 1
+    }
+
+    pub fn get_style_index(&self, style: &Style) -> Option<i32> {
+        for (index, cell_xf) in self.cell_xfs.iter().enumerate() {
+            let border_id = cell_xf.border_id as usize;
+            let fill_id = cell_xf.fill_id as usize;
+            let font_id = cell_xf.font_id as usize;
+            let num_fmt_id = cell_xf.num_fmt_id;
+            let quote_prefix = cell_xf.quote_prefix;
+            if style
+                == &(Style {
+                    alignment: cell_xf.alignment.clone(),
+                    num_fmt: get_num_fmt(num_fmt_id, &self.num_fmts),
+                    fill: self.fills[fill_id].clone(),
+                    font: self.fonts[font_id].clone(),
+                    border: self.borders[border_id].clone(),
+                    quote_prefix,
+                })
+            {
+                return Some(index as i32);
+            }
+        }
+        None
+    }
+
+    pub(crate) fn get_style_index_or_create(&mut self, style: &Style) -> i32 {
+        // Check if style exist. If so sets style cell number to that otherwise create a new style.
+        if let Some(index) = self.get_style_index(style) {
+            index
+        } else {
+            self.create_new_style(style)
+        }
+    }
+
+    /// Adds a named cell style from an existing index
+    /// Fails if the named style already exists or if there is not a style with that index
+    pub fn add_named_cell_style(
+        &mut self,
+        style_name: &str,
+        style_index: i32,
+    ) -> Result<(), String> {
+        if self.get_style_index_by_name(style_name).is_ok() {
+            return Err("A style with that name already exists".to_string());
+        }
+        if self.cell_xfs.len() < style_index as usize {
+            return Err("There is no style with that index".to_string());
+        }
+        let cell_style = CellStyles {
+            name: style_name.to_string(),
+            xf_id: style_index,
+            builtin_id: 0,
+        };
+        self.cell_styles.push(cell_style);
+        Ok(())
+    }
+
+    // Returns the index of the style or fails.
+    // NB: this method is case sensitive
+    pub fn get_style_index_by_name(&self, style_name: &str) -> Result<i32, String> {
+        for cell_style in &self.cell_styles {
+            if cell_style.name == style_name {
+                return Ok(cell_style.xf_id);
+            }
+        }
+        Err(format!("Style '{}' not found", style_name))
+    }
+
+    pub fn create_named_style(&mut self, style_name: &str, style: &Style) -> Result<(), String> {
+        let style_index = self.create_new_style(style);
+        self.add_named_cell_style(style_name, style_index)?;
+        Ok(())
+    }
+
+    pub(crate) fn get_style_with_quote_prefix(&mut self, index: i32) -> i32 {
+        let mut style = self.get_style(index);
+        style.quote_prefix = true;
+        self.get_style_index_or_create(&style)
+    }
+
+    pub(crate) fn get_style_with_format(&mut self, index: i32, num_fmt: &str) -> i32 {
+        let mut style = self.get_style(index);
+        style.num_fmt = num_fmt.to_string();
+        self.get_style_index_or_create(&style)
+    }
+
+    pub(crate) fn get_style_without_quote_prefix(&mut self, index: i32) -> i32 {
+        let mut style = self.get_style(index);
+        style.quote_prefix = false;
+        self.get_style_index_or_create(&style)
+    }
+
+    pub(crate) fn style_is_quote_prefix(&self, index: i32) -> bool {
+        let cell_xf = &self.cell_xfs[index as usize];
+        cell_xf.quote_prefix
+    }
+
+    pub(crate) fn get_style(&self, index: i32) -> Style {
+        let cell_xf = &self.cell_xfs[index as usize];
+
+        let border_id = cell_xf.border_id as usize;
+        let fill_id = cell_xf.fill_id as usize;
+        let font_id = cell_xf.font_id as usize;
+        let num_fmt_id = cell_xf.num_fmt_id;
+        let quote_prefix = cell_xf.quote_prefix;
+        let alignment = cell_xf.alignment.clone();
+
+        Style {
+            alignment,
+            num_fmt: get_num_fmt(num_fmt_id, &self.num_fmts),
+            fill: self.fills[fill_id].clone(),
+            font: self.fonts[font_id].clone(),
+            border: self.borders[border_id].clone(),
+            quote_prefix,
+        }
+    }
+}
+
+// TODO: Try to find a better spot for styles setters
+impl Model {
+    pub fn set_cell_style(
+        &mut self,
+        sheet: u32,
+        row: i32,
+        column: i32,
+        style: &Style,
+    ) -> Result<(), String> {
+        let style_index = self.workbook.styles.get_style_index_or_create(style);
+        self.workbook
+            .worksheet_mut(sheet)?
+            .set_cell_style(row, column, style_index);
+        Ok(())
+    }
+
+    pub fn copy_cell_style(
+        &mut self,
+        source_cell: (u32, i32, i32),
+        destination_cell: (u32, i32, i32),
+    ) -> Result<(), String> {
+        let source_style_index = self
+            .workbook
+            .worksheet(source_cell.0)?
+            .get_style(source_cell.1, source_cell.2);
+
+        self.workbook
+            .worksheet_mut(destination_cell.0)?
+            .set_cell_style(destination_cell.1, destination_cell.2, source_style_index);
+
+        Ok(())
+    }
+
+    /// Sets the style "style_name" in cell
+    pub fn set_cell_style_by_name(
+        &mut self,
+        sheet: u32,
+        row: i32,
+        column: i32,
+        style_name: &str,
+    ) -> Result<(), String> {
+        let style_index = self.workbook.styles.get_style_index_by_name(style_name)?;
+        self.workbook
+            .worksheet_mut(sheet)?
+            .set_cell_style(row, column, style_index);
+        Ok(())
+    }
+
+    pub fn set_sheet_style(&mut self, sheet: u32, style_name: &str) -> Result<(), String> {
+        let style_index = self.workbook.styles.get_style_index_by_name(style_name)?;
+        self.workbook.worksheet_mut(sheet)?.set_style(style_index)?;
+        Ok(())
+    }
+
+    pub fn set_sheet_row_style(
+        &mut self,
+        sheet: u32,
+        row: i32,
+        style_name: &str,
+    ) -> Result<(), String> {
+        let style_index = self.workbook.styles.get_style_index_by_name(style_name)?;
+        self.workbook
+            .worksheet_mut(sheet)?
+            .set_row_style(row, style_index)?;
+        Ok(())
+    }
+
+    pub fn set_sheet_column_style(
+        &mut self,
+        sheet: u32,
+        column: i32,
+        style_name: &str,
+    ) -> Result<(), String> {
+        let style_index = self.workbook.styles.get_style_index_by_name(style_name)?;
+        self.workbook
+            .worksheet_mut(sheet)?
+            .set_column_style(column, style_index)?;
+        Ok(())
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/types.rs.html b/src/ironcalc_base/types.rs.html new file mode 100644 index 0000000..9d1dd52 --- /dev/null +++ b/src/ironcalc_base/types.rs.html @@ -0,0 +1,1327 @@ +types.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+
use serde::{Deserialize, Serialize};
+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<Alignment>) -> bool {
+    o.is_none() || *o == Some(Alignment::default())
+}
+
+fn hashmap_is_empty(h: &HashMap<String, Table>) -> bool {
+    h.values().len() == 0
+}
+
+#[derive(Serialize, Deserialize, 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(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
+pub struct WorkbookSettings {
+    pub tz: String,
+    pub locale: String,
+}
+/// An internal representation of an IronCalc Workbook
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+#[serde(deny_unknown_fields)]
+pub struct Workbook {
+    pub shared_strings: Vec<String>,
+    pub defined_names: Vec<DefinedName>,
+    pub worksheets: Vec<Worksheet>,
+    pub styles: Styles,
+    pub name: String,
+    pub settings: WorkbookSettings,
+    pub metadata: Metadata,
+    #[serde(default)]
+    #[serde(skip_serializing_if = "hashmap_is_empty")]
+    pub tables: HashMap<String, Table>,
+}
+
+/// A defined name. The `sheet_id` is the sheet index in case the name is local
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
+pub struct DefinedName {
+    pub name: String,
+    pub formula: String,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub sheet_id: Option<u32>,
+}
+
+// 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(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
+#[serde(rename_all = "lowercase")]
+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"),
+        }
+    }
+}
+
+/// Internal representation of a worksheet Excel object
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+pub struct Worksheet {
+    pub dimension: String,
+    pub cols: Vec<Col>,
+    pub rows: Vec<Row>,
+    pub name: String,
+    pub sheet_data: SheetData,
+    pub shared_formulas: Vec<String>,
+    pub sheet_id: u32,
+    pub state: SheetState,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub color: Option<String>,
+    pub merge_cells: Vec<String>,
+    pub comments: Vec<Comment>,
+    #[serde(default)]
+    #[serde(skip_serializing_if = "is_zero")]
+    pub frozen_rows: i32,
+    #[serde(default)]
+    #[serde(skip_serializing_if = "is_zero")]
+    pub frozen_columns: i32,
+}
+
+/// 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<i32, HashMap<i32, Cell>>;
+
+// ECMA-376-1:2016 section 18.3.1.73
+#[derive(Serialize, Deserialize, 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,
+    #[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, 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<i32>,
+}
+
+/// 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(Serialize, Deserialize, Debug, Clone, PartialEq)]
+#[serde(tag = "t", deny_unknown_fields)]
+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 },
+    // Maybe we should not have this type. In Excel this is just a string
+    #[serde(rename = "e")]
+    ErrorCell { ei: Error, s: i32 },
+    // Always a shared string
+    #[serde(rename = "s")]
+    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 },
+    // always inline string
+    #[serde(rename = "str")]
+    CellFormulaString { f: i32, v: String, s: i32 },
+    #[serde(rename = "fe")]
+    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(Serialize, Deserialize, 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<String>,
+    pub cell_ref: String,
+}
+
+// ECMA-376-1:2016 section 18.5.1.2
+#[derive(Serialize, Deserialize, 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,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub header_row_dxf_id: Option<u32>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub data_dxf_id: Option<u32>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub totals_row_dxf_id: Option<u32>,
+    pub columns: Vec<TableColumn>,
+    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, 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<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub header_row_dxf_id: Option<u32>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub data_dxf_id: Option<u32>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub totals_row_dxf_id: Option<u32>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub totals_row_function: Option<String>,
+}
+
+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(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
+pub struct TableStyleInfo {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub name: Option<String>,
+    #[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, Debug, PartialEq, Eq, Clone)]
+pub struct Styles {
+    pub num_fmts: Vec<NumFmt>,
+    pub fonts: Vec<Font>,
+    pub fills: Vec<Fill>,
+    pub borders: Vec<Border>,
+    pub cell_style_xfs: Vec<CellStyleXfs>,
+    pub cell_xfs: Vec<CellXfs>,
+    pub cell_styles: Vec<CellStyles>,
+}
+
+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, 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, 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, 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<String>,
+    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, Debug, PartialEq, Eq, Clone)]
+pub struct Fill {
+    pub pattern_type: String,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub fg_color: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub bg_color: Option<String>,
+}
+
+impl Default for Fill {
+    fn default() -> Self {
+        Fill {
+            pattern_type: "none".to_string(),
+            fg_color: Default::default(),
+            bg_color: Default::default(),
+        }
+    }
+}
+
+#[derive(Serialize, Deserialize, 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, 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, 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(Serialize, Deserialize, 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,
+}
+
+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(Serialize, Deserialize, 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<Alignment>,
+}
+
+#[derive(Serialize, Deserialize, 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, 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, Debug, PartialEq, Eq, Clone)]
+pub struct BorderItem {
+    pub style: BorderStyle,
+    pub color: Option<String>,
+}
+
+#[derive(Serialize, Deserialize, 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<BorderItem>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub right: Option<BorderItem>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub top: Option<BorderItem>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub bottom: Option<BorderItem>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub diagonal: Option<BorderItem>,
+}
+
+/// Information need to show a sheet tab in the UI
+/// The color is serialized only if it is not Color::None
+#[derive(Serialize, Deserialize, Debug, PartialEq)]
+pub struct SheetInfo {
+    pub name: String,
+    pub state: String,
+    pub sheet_id: u32,
+    pub color: Option<String>,
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/units.rs.html b/src/ironcalc_base/units.rs.html new file mode 100644 index 0000000..48ed988 --- /dev/null +++ b/src/ironcalc_base/units.rs.html @@ -0,0 +1,729 @@ +units.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+
use crate::{
+    calc_result::CellReference,
+    expressions::{parser::Node, token::OpProduct},
+    formatter::parser::{ParsePart, Parser},
+    functions::Function,
+    model::Model,
+};
+
+pub enum Units {
+    Number {
+        group_separator: bool,
+        precision: i32,
+        num_fmt: String,
+    },
+    Currency {
+        group_separator: bool,
+        precision: i32,
+        num_fmt: String,
+        currency: String,
+    },
+    Percentage {
+        group_separator: bool,
+        precision: i32,
+        num_fmt: String,
+    },
+    Date(String),
+}
+
+impl Units {
+    pub fn get_num_fmt(&self) -> String {
+        match self {
+            Units::Number { num_fmt, .. } => num_fmt.to_string(),
+            Units::Currency { num_fmt, .. } => num_fmt.to_string(),
+            Units::Percentage { num_fmt, .. } => num_fmt.to_string(),
+            Units::Date(num_fmt) => num_fmt.to_string(),
+        }
+    }
+    pub fn get_precision(&self) -> i32 {
+        match self {
+            Units::Number { precision, .. } => *precision,
+            Units::Currency { precision, .. } => *precision,
+            Units::Percentage { precision, .. } => *precision,
+            Units::Date(_) => 0,
+        }
+    }
+}
+
+fn get_units_from_format_string(num_fmt: &str) -> Option<Units> {
+    let mut parser = Parser::new(num_fmt);
+    parser.parse();
+    // We only care about the first part (positive number)
+    match &parser.parts[0] {
+        ParsePart::Number(part) => {
+            if part.percent > 0 {
+                Some(Units::Percentage {
+                    num_fmt: num_fmt.to_string(),
+                    group_separator: part.use_thousands,
+                    precision: part.precision,
+                })
+            } else if num_fmt.contains('$') {
+                Some(Units::Currency {
+                    num_fmt: num_fmt.to_string(),
+                    group_separator: part.use_thousands,
+                    precision: part.precision,
+                    currency: "$".to_string(),
+                })
+            } else if num_fmt.contains('€') {
+                Some(Units::Currency {
+                    num_fmt: num_fmt.to_string(),
+                    group_separator: part.use_thousands,
+                    precision: part.precision,
+                    currency: "€".to_string(),
+                })
+            } else {
+                Some(Units::Number {
+                    num_fmt: num_fmt.to_string(),
+                    group_separator: part.use_thousands,
+                    precision: part.precision,
+                })
+            }
+        }
+        ParsePart::Date(_) => Some(Units::Date(num_fmt.to_string())),
+        ParsePart::Error(_) => None,
+        ParsePart::General(_) => None,
+    }
+}
+
+impl Model {
+    fn compute_cell_units(&self, cell_reference: &CellReference) -> Option<Units> {
+        let style = &self.get_style_for_cell(
+            cell_reference.sheet,
+            cell_reference.row,
+            cell_reference.column,
+        );
+        get_units_from_format_string(&style.num_fmt)
+    }
+
+    pub(crate) fn compute_node_units(&self, node: &Node, cell: &CellReference) -> Option<Units> {
+        match node {
+            Node::ReferenceKind {
+                sheet_name: _,
+                sheet_index,
+                absolute_row,
+                absolute_column,
+                row,
+                column,
+            } => {
+                let mut row1 = *row;
+                let mut column1 = *column;
+                if !absolute_row {
+                    row1 += cell.row;
+                }
+                if !absolute_column {
+                    column1 += cell.column;
+                }
+                self.compute_cell_units(&CellReference {
+                    sheet: *sheet_index,
+                    row: row1,
+                    column: column1,
+                })
+            }
+            Node::RangeKind {
+                sheet_name: _,
+                sheet_index,
+                absolute_row1,
+                absolute_column1,
+                row1,
+                column1,
+                absolute_row2: _,
+                absolute_column2: _,
+                row2: _,
+                column2: _,
+            } => {
+                // We return the unit of the first element
+                let mut row1 = *row1;
+                let mut column1 = *column1;
+                if !absolute_row1 {
+                    row1 += cell.row;
+                }
+                if !absolute_column1 {
+                    column1 += cell.column;
+                }
+                self.compute_cell_units(&CellReference {
+                    sheet: *sheet_index,
+                    row: row1,
+                    column: column1,
+                })
+            }
+            Node::OpSumKind {
+                kind: _,
+                left,
+                right,
+            } => {
+                let left_units = self.compute_node_units(left, cell);
+                let right_units = self.compute_node_units(right, cell);
+                match (&left_units, &right_units) {
+                    (Some(_), None) => left_units,
+                    (None, Some(_)) => right_units,
+                    (Some(l), Some(r)) => {
+                        if l.get_precision() < r.get_precision() {
+                            right_units
+                        } else {
+                            left_units
+                        }
+                    }
+                    (None, None) => None,
+                }
+            }
+            Node::OpProductKind { kind, left, right } => {
+                let left_units = self.compute_node_units(left, cell);
+                let right_units = self.compute_node_units(right, cell);
+                match (&left_units, &right_units) {
+                    (
+                        Some(Units::Percentage { precision: l, .. }),
+                        Some(Units::Percentage { precision: r, .. }),
+                    ) => {
+                        if l > r {
+                            left_units
+                        } else {
+                            if *r > 1 {
+                                return right_units;
+                            }
+                            // When multiplying percentage we want at least two decimal places
+                            Some(Units::Percentage {
+                                group_separator: false,
+                                precision: 2,
+                                num_fmt: "0.00%".to_string(),
+                            })
+                        }
+                    }
+                    (
+                        Some(Units::Currency {
+                            currency,
+                            precision,
+                            ..
+                        }),
+                        Some(Units::Percentage { .. }),
+                    ) => {
+                        match kind {
+                            OpProduct::Divide => None,
+                            OpProduct::Times => {
+                                if *precision > 1 {
+                                    return left_units;
+                                }
+                                // This is tricky, we need at least 2 digit precision
+                                // but I do not want to mess with the num_fmt string
+                                Some(Units::Currency {
+                                    currency: currency.to_string(),
+                                    group_separator: true,
+                                    precision: 2,
+                                    num_fmt: format!("{currency}#,##0.00"),
+                                })
+                            }
+                        }
+                    }
+                    (
+                        Some(Units::Percentage { .. }),
+                        Some(Units::Currency {
+                            precision,
+                            currency,
+                            ..
+                        }),
+                    ) => {
+                        match kind {
+                            OpProduct::Divide => None,
+                            OpProduct::Times => {
+                                if *precision > 1 {
+                                    return right_units;
+                                }
+                                // This is tricky, we need at least 2 digit precision
+                                // but I do not want to mess with the num_fmt string
+                                Some(Units::Currency {
+                                    currency: currency.to_string(),
+                                    group_separator: true,
+                                    precision: 2,
+                                    num_fmt: format!("{currency}#,##0.00"),
+                                })
+                            }
+                        }
+                    }
+                    (Some(Units::Percentage { .. }), _) => right_units,
+                    (_, Some(Units::Percentage { .. })) => match kind {
+                        OpProduct::Divide => None,
+                        OpProduct::Times => left_units,
+                    },
+                    (None, _) => match kind {
+                        OpProduct::Divide => None,
+                        OpProduct::Times => right_units,
+                    },
+                    (_, None) => left_units,
+                    (
+                        Some(Units::Number { precision: l, .. }),
+                        Some(Units::Number { precision: r, .. }),
+                    ) => {
+                        if l > r {
+                            left_units
+                        } else {
+                            right_units
+                        }
+                    }
+                    (Some(Units::Number { .. }), _) => match kind {
+                        OpProduct::Divide => None,
+                        OpProduct::Times => right_units,
+                    },
+                    (_, Some(Units::Number { .. })) => left_units,
+                    _ => None,
+                }
+            }
+            Node::FunctionKind { kind, args } => self.compute_function_units(kind, args, cell),
+            Node::UnaryKind { kind: _, right } => {
+                // What happens if kind => OpUnary::Percentage?
+                self.compute_node_units(right, cell)
+            }
+            // The rest of the nodes return None
+            Node::BooleanKind(_) => None,
+            Node::NumberKind(_) => None,
+            Node::StringKind(_) => None,
+            Node::WrongReferenceKind { .. } => None,
+            Node::WrongRangeKind { .. } => None,
+            Node::OpRangeKind { .. } => None,
+            Node::OpConcatenateKind { .. } => None,
+            Node::ErrorKind(_) => None,
+            Node::ParseErrorKind { .. } => None,
+            Node::EmptyArgKind => None,
+            Node::InvalidFunctionKind { .. } => None,
+            Node::ArrayKind(_) => None,
+            Node::VariableKind(_) => None,
+            Node::CompareKind { .. } => None,
+            Node::OpPowerKind { .. } => None,
+        }
+    }
+
+    fn compute_function_units(
+        &self,
+        kind: &Function,
+        args: &[Node],
+        cell: &CellReference,
+    ) -> Option<Units> {
+        match kind {
+            Function::Sum => self.units_fn_sum_like(args, cell),
+            Function::Average => self.units_fn_sum_like(args, cell),
+            Function::Pmt => self.units_fn_currency(args, cell),
+            Function::Nper => self.units_fn_currency(args, cell),
+            Function::Npv => self.units_fn_currency(args, cell),
+            Function::Irr => self.units_fn_percentage(args, cell),
+            Function::Mirr => self.units_fn_percentage(args, cell),
+            Function::Sln => self.units_fn_currency(args, cell),
+            Function::Syd => self.units_fn_currency(args, cell),
+            Function::Db => self.units_fn_currency(args, cell),
+            Function::Ddb => self.units_fn_currency(args, cell),
+            Function::Cumipmt => self.units_fn_currency(args, cell),
+            Function::Cumprinc => self.units_fn_currency(args, cell),
+            Function::Tbilleq => self.units_fn_percentage_2(args, cell),
+            Function::Tbillprice => self.units_fn_currency(args, cell),
+            Function::Tbillyield => self.units_fn_percentage_2(args, cell),
+            Function::Date => self.units_fn_dates(args, cell),
+            Function::Today => self.units_fn_dates(args, cell),
+            _ => None,
+        }
+    }
+
+    fn units_fn_sum_like(&self, args: &[Node], cell: &CellReference) -> Option<Units> {
+        // We return the unit of the first argument
+        if !args.is_empty() {
+            return self.compute_node_units(&args[0], cell);
+        }
+        None
+    }
+
+    fn units_fn_currency(&self, _args: &[Node], _cell: &CellReference) -> Option<Units> {
+        let currency_symbol = &self.locale.currency.symbol;
+        let standard_format = &self.locale.numbers.currency_formats.standard;
+        let num_fmt = standard_format.replace('¤', currency_symbol);
+        // The "space" in the cldr is a weird space.
+        let num_fmt = num_fmt.replace(' ', " ");
+        Some(Units::Currency {
+            num_fmt,
+            group_separator: true,
+            precision: 2,
+            currency: currency_symbol.to_string(),
+        })
+    }
+
+    fn units_fn_percentage(&self, _args: &[Node], _cell: &CellReference) -> Option<Units> {
+        Some(Units::Percentage {
+            group_separator: false,
+            precision: 0,
+            num_fmt: "0%".to_string(),
+        })
+    }
+
+    fn units_fn_percentage_2(&self, _args: &[Node], _cell: &CellReference) -> Option<Units> {
+        Some(Units::Percentage {
+            group_separator: false,
+            precision: 2,
+            num_fmt: "0.00%".to_string(),
+        })
+    }
+
+    fn units_fn_dates(&self, _args: &[Node], _cell: &CellReference) -> Option<Units> {
+        // TODO: update locale and use it here
+        Some(Units::Date("dd/mm/yyyy".to_string()))
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/utils.rs.html b/src/ironcalc_base/utils.rs.html new file mode 100644 index 0000000..92da700 --- /dev/null +++ b/src/ironcalc_base/utils.rs.html @@ -0,0 +1,739 @@ +utils.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+
use crate::expressions::token::get_error_by_name;
+use crate::language::Language;
+
+use crate::{
+    calc_result::CellReference,
+    expressions::{
+        lexer::{Lexer, LexerMode},
+        token::TokenType,
+    },
+    language::get_language,
+    locale::Locale,
+};
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum ParsedReference {
+    CellReference(CellReference),
+    Range(CellReference, CellReference),
+}
+
+impl ParsedReference {
+    /// Parses reference in formula format. For example:  `Sheet1!A1`, `Sheet1!$A$1:$B$9`.
+    /// Absolute references (`$`) do not affect parsing.
+    ///
+    /// # Arguments
+    ///
+    /// * `sheet_index_context` - if available, sheet index can be provided so references
+    ///   without explicit sheet name can be recognized
+    /// * `reference` - text string to parse as reference
+    /// * `locale` - locale that will be used to set-up parser
+    /// * `get_sheet_index_by_name` - function that allows to translate sheet name to index
+    pub(crate) fn parse_reference_formula<F: Fn(&str) -> Option<u32>>(
+        sheet_index_context: Option<u32>,
+        reference: &str,
+        locale: &Locale,
+        get_sheet_index_by_name: F,
+    ) -> Result<ParsedReference, String> {
+        let language = get_language("en").expect("");
+        let mut lexer = Lexer::new(reference, LexerMode::A1, locale, language);
+
+        let reference_token = lexer.next_token();
+        let eof_token = lexer.next_token();
+
+        if TokenType::EOF != eof_token {
+            return Err("Invalid reference. Expected only one token.".to_string());
+        }
+
+        match reference_token {
+            TokenType::Reference {
+                sheet: sheet_name,
+                column: column_id,
+                row: row_id,
+                ..
+            } => {
+                let sheet_index;
+                if let Some(name) = sheet_name {
+                    match get_sheet_index_by_name(&name) {
+                        Some(i) => sheet_index = i,
+                        None => {
+                            return Err(format!(
+                                "Invalid reference. Sheet \"{}\" could not be found.",
+                                name.as_str(),
+                            ));
+                        }
+                    }
+                } else if let Some(sheet_index_context) = sheet_index_context {
+                    sheet_index = sheet_index_context;
+                } else {
+                    return Err(
+                        "Reference doesn't contain sheet name and relative cell is not known."
+                            .to_string(),
+                    );
+                }
+
+                Ok(ParsedReference::CellReference(CellReference {
+                    sheet: sheet_index,
+                    row: row_id,
+                    column: column_id,
+                }))
+            }
+            TokenType::Range {
+                sheet: sheet_name,
+                left,
+                right,
+            } => {
+                let sheet_index;
+                if let Some(name) = sheet_name {
+                    match get_sheet_index_by_name(&name) {
+                        Some(i) => sheet_index = i,
+                        None => {
+                            return Err(format!(
+                                "Invalid reference. Sheet \"{}\" could not be found.",
+                                name.as_str(),
+                            ));
+                        }
+                    }
+                } else if let Some(sheet_index_context) = sheet_index_context {
+                    sheet_index = sheet_index_context;
+                } else {
+                    return Err(
+                        "Reference doesn't contain sheet name and relative cell is not known."
+                            .to_string(),
+                    );
+                }
+
+                Ok(ParsedReference::Range(
+                    CellReference {
+                        sheet: sheet_index,
+                        row: left.row,
+                        column: left.column,
+                    },
+                    CellReference {
+                        sheet: sheet_index,
+                        row: right.row,
+                        column: right.column,
+                    },
+                ))
+            }
+            _ => Err("Invalid reference. First token is not a reference.".to_string()),
+        }
+    }
+}
+
+/// Returns true if the string value could be interpreted as:
+///  * a formula
+///  * a number
+///  * a boolean
+///  * an error (i.e "#VALUE!")
+pub(crate) fn value_needs_quoting(value: &str, language: &Language) -> bool {
+    value.starts_with('=')
+        || value.parse::<f64>().is_ok()
+        || value.to_lowercase().parse::<bool>().is_ok()
+        || get_error_by_name(&value.to_uppercase(), language).is_some()
+}
+
+/// Valid hex colors are #FFAABB
+/// #fff is not valid
+pub(crate) fn is_valid_hex_color(color: &str) -> bool {
+    if color.chars().count() != 7 {
+        return false;
+    }
+    if !color.starts_with('#') {
+        return false;
+    }
+    if let Ok(z) = i32::from_str_radix(&color[1..], 16) {
+        if (0..=0xffffff).contains(&z) {
+            return true;
+        }
+    }
+    false
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::language::get_language;
+    use crate::locale::{get_locale, Locale};
+
+    fn get_test_locale() -> &'static Locale {
+        #![allow(clippy::unwrap_used)]
+        get_locale("en").unwrap()
+    }
+
+    fn get_sheet_index_by_name(sheet_names: &[&str], name: &str) -> Option<u32> {
+        sheet_names
+            .iter()
+            .position(|&sheet_name| sheet_name == name)
+            .map(|index| index as u32)
+    }
+
+    #[test]
+    fn test_parse_cell_references() {
+        let locale = get_test_locale();
+        let sheet_names = vec!["Sheet1", "Sheet2", "Sheet3"];
+
+        assert_eq!(
+            ParsedReference::parse_reference_formula(Some(7), "A1", locale, |name| {
+                get_sheet_index_by_name(&sheet_names, name)
+            },),
+            Ok(ParsedReference::CellReference(CellReference {
+                sheet: 7,
+                row: 1,
+                column: 1,
+            })),
+        );
+
+        assert_eq!(
+            ParsedReference::parse_reference_formula(None, "Sheet1!A1", locale, |name| {
+                get_sheet_index_by_name(&sheet_names, name)
+            },),
+            Ok(ParsedReference::CellReference(CellReference {
+                sheet: 0,
+                row: 1,
+                column: 1,
+            })),
+        );
+
+        assert_eq!(
+            ParsedReference::parse_reference_formula(None, "Sheet1!$A$1", locale, |name| {
+                get_sheet_index_by_name(&sheet_names, name)
+            },),
+            Ok(ParsedReference::CellReference(CellReference {
+                sheet: 0,
+                row: 1,
+                column: 1,
+            })),
+        );
+
+        assert_eq!(
+            ParsedReference::parse_reference_formula(None, "Sheet2!$A$1", locale, |name| {
+                get_sheet_index_by_name(&sheet_names, name)
+            },),
+            Ok(ParsedReference::CellReference(CellReference {
+                sheet: 1,
+                row: 1,
+                column: 1,
+            })),
+        );
+    }
+
+    #[test]
+    fn test_parse_range_references() {
+        let locale = get_test_locale();
+        let sheet_names = vec!["Sheet1", "Sheet2", "Sheet3"];
+
+        assert_eq!(
+            ParsedReference::parse_reference_formula(Some(5), "A1:A2", locale, |name| {
+                get_sheet_index_by_name(&sheet_names, name)
+            },),
+            Ok(ParsedReference::Range(
+                CellReference {
+                    sheet: 5,
+                    column: 1,
+                    row: 1,
+                },
+                CellReference {
+                    sheet: 5,
+                    column: 1,
+                    row: 2,
+                },
+            )),
+        );
+
+        assert_eq!(
+            ParsedReference::parse_reference_formula(None, "Sheet1!$A$1:$B$10", locale, |name| {
+                get_sheet_index_by_name(&sheet_names, name)
+            },),
+            Ok(ParsedReference::Range(
+                CellReference {
+                    sheet: 0,
+                    row: 1,
+                    column: 1,
+                },
+                CellReference {
+                    sheet: 0,
+                    row: 10,
+                    column: 2,
+                },
+            )),
+        );
+
+        assert_eq!(
+            ParsedReference::parse_reference_formula(None, "Sheet2!AA1:E$11", locale, |name| {
+                get_sheet_index_by_name(&sheet_names, name)
+            },),
+            Ok(ParsedReference::Range(
+                CellReference {
+                    sheet: 1,
+                    row: 1,
+                    column: 27,
+                },
+                CellReference {
+                    sheet: 1,
+                    row: 11,
+                    column: 5,
+                },
+            )),
+        );
+    }
+
+    #[test]
+    fn test_error_reject_assignments() {
+        let locale = get_test_locale();
+        let sheet_index = Some(1);
+        assert_eq!(
+            ParsedReference::parse_reference_formula(sheet_index, "=A1", locale, |_| Some(1)),
+            Err("Invalid reference. Expected only one token.".to_string()),
+        );
+        assert_eq!(
+            ParsedReference::parse_reference_formula(sheet_index, "=$A$1", locale, |_| { Some(1) }),
+            Err("Invalid reference. Expected only one token.".to_string()),
+        );
+        assert_eq!(
+            ParsedReference::parse_reference_formula(None, "=Sheet1!A1", locale, |_| Some(1)),
+            Err("Invalid reference. Expected only one token.".to_string()),
+        );
+    }
+
+    #[test]
+    fn test_error_reject_formulas_without_equal_sign() {
+        let locale = get_test_locale();
+        assert_eq!(
+            ParsedReference::parse_reference_formula(None, "SUM", locale, |_| Some(1)),
+            Err("Invalid reference. First token is not a reference.".to_string()),
+        );
+        assert_eq!(
+            ParsedReference::parse_reference_formula(None, "SUM(A1:A2)", locale, |_| Some(1)),
+            Err("Invalid reference. Expected only one token.".to_string()),
+        );
+    }
+
+    #[test]
+    fn test_error_reject_without_sheet_and_relative_cell() {
+        let locale = get_test_locale();
+        assert_eq!(
+            ParsedReference::parse_reference_formula(None, "A1", locale, |_| Some(1)),
+            Err("Reference doesn't contain sheet name and relative cell is not known.".to_string()),
+        );
+        assert_eq!(
+            ParsedReference::parse_reference_formula(None, "A1:A2", locale, |_| Some(1)),
+            Err("Reference doesn't contain sheet name and relative cell is not known.".to_string()),
+        );
+    }
+
+    #[test]
+    fn test_error_unrecognized_sheet_name() {
+        let locale = get_test_locale();
+        assert_eq!(
+            ParsedReference::parse_reference_formula(None, "SheetName!A1", locale, |_| None),
+            Err("Invalid reference. Sheet \"SheetName\" could not be found.".to_string()),
+        );
+        assert_eq!(
+            ParsedReference::parse_reference_formula(None, "SheetName2!A1:A4", locale, |_| None),
+            Err("Invalid reference. Sheet \"SheetName2\" could not be found.".to_string()),
+        );
+    }
+
+    #[test]
+    fn test_value_needs_quoting() {
+        let en_language = get_language("en").expect("en language expected");
+
+        assert!(!value_needs_quoting("", en_language));
+        assert!(!value_needs_quoting("hello", en_language));
+
+        assert!(value_needs_quoting("12", en_language));
+        assert!(value_needs_quoting("true", en_language));
+        assert!(value_needs_quoting("False", en_language));
+
+        assert!(value_needs_quoting("=A1", en_language));
+
+        assert!(value_needs_quoting("#REF!", en_language));
+        assert!(value_needs_quoting("#NAME?", en_language));
+    }
+
+    #[test]
+    fn test_is_valid_hex_color() {
+        assert!(is_valid_hex_color("#000000"));
+        assert!(is_valid_hex_color("#ffffff"));
+
+        assert!(!is_valid_hex_color("000000"));
+        assert!(!is_valid_hex_color("ffffff"));
+
+        assert!(!is_valid_hex_color("#gggggg"));
+
+        // Not obvious cases unrecognized as colors
+        assert!(!is_valid_hex_color("#ffffff "));
+        assert!(!is_valid_hex_color("#fff")); // CSS shorthand
+        assert!(!is_valid_hex_color("#ffffff00")); // with alpha channel
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/workbook.rs.html b/src/ironcalc_base/workbook.rs.html new file mode 100644 index 0000000..6b3f90b --- /dev/null +++ b/src/ironcalc_base/workbook.rs.html @@ -0,0 +1,85 @@ +workbook.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+
use std::vec::Vec;
+
+use crate::types::*;
+
+impl Workbook {
+    pub fn get_worksheet_names(&self) -> Vec<String> {
+        self.worksheets
+            .iter()
+            .map(|worksheet| worksheet.get_name())
+            .collect()
+    }
+    pub fn get_worksheet_ids(&self) -> Vec<u32> {
+        self.worksheets
+            .iter()
+            .map(|worksheet| worksheet.get_sheet_id())
+            .collect()
+    }
+
+    pub fn worksheet(&self, worksheet_index: u32) -> Result<&Worksheet, String> {
+        self.worksheets
+            .get(worksheet_index as usize)
+            .ok_or_else(|| "Invalid sheet index".to_string())
+    }
+
+    pub fn worksheet_mut(&mut self, worksheet_index: u32) -> Result<&mut Worksheet, String> {
+        self.worksheets
+            .get_mut(worksheet_index as usize)
+            .ok_or_else(|| "Invalid sheet index".to_string())
+    }
+
+    pub fn get_worksheets_info(&self) -> Vec<SheetInfo> {
+        self.worksheets
+            .iter()
+            .map(|worksheet| SheetInfo {
+                name: worksheet.get_name(),
+                state: worksheet.state.to_string(),
+                color: worksheet.color.clone(),
+                sheet_id: worksheet.sheet_id,
+            })
+            .collect()
+    }
+}
+
\ No newline at end of file diff --git a/src/ironcalc_base/worksheet.rs.html b/src/ironcalc_base/worksheet.rs.html new file mode 100644 index 0000000..8237832 --- /dev/null +++ b/src/ironcalc_base/worksheet.rs.html @@ -0,0 +1,1261 @@ +worksheet.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+
use crate::constants::{self, LAST_COLUMN, LAST_ROW};
+use crate::expressions::types::CellReferenceIndex;
+use crate::expressions::utils::{is_valid_column_number, is_valid_row};
+use crate::{expressions::token::Error, types::*};
+
+use std::collections::HashMap;
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct WorksheetDimension {
+    pub min_row: i32,
+    pub max_row: i32,
+    pub min_column: i32,
+    pub max_column: i32,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum NavigationDirection {
+    Left,
+    Right,
+    Up,
+    Down,
+}
+
+impl Worksheet {
+    pub fn get_name(&self) -> String {
+        self.name.clone()
+    }
+
+    pub fn get_sheet_id(&self) -> u32 {
+        self.sheet_id
+    }
+
+    pub fn set_name(&mut self, name: &str) {
+        self.name = name.to_string();
+    }
+
+    pub fn cell(&self, row: i32, column: i32) -> Option<&Cell> {
+        self.sheet_data.get(&row)?.get(&column)
+    }
+
+    pub(crate) fn cell_mut(&mut self, row: i32, column: i32) -> Option<&mut Cell> {
+        self.sheet_data.get_mut(&row)?.get_mut(&column)
+    }
+
+    fn update_cell(&mut self, row: i32, column: i32, new_cell: Cell) {
+        match self.sheet_data.get_mut(&row) {
+            Some(column_data) => match column_data.get(&column) {
+                Some(_cell) => {
+                    column_data.insert(column, new_cell);
+                }
+                None => {
+                    column_data.insert(column, new_cell);
+                }
+            },
+            None => {
+                let mut column_data = HashMap::new();
+                column_data.insert(column, new_cell);
+                self.sheet_data.insert(row, column_data);
+            }
+        }
+    }
+
+    // TODO [MVP]: Pass the cell style from the model
+    // See: get_style_for_cell
+    fn get_row_column_style(&self, row_index: i32, column_index: i32) -> i32 {
+        let rows = &self.rows;
+        for row in rows {
+            if row.r == row_index {
+                if row.custom_format {
+                    return row.s;
+                } else {
+                    break;
+                }
+            }
+        }
+        let cols = &self.cols;
+        for column in cols.iter() {
+            let min = column.min;
+            let max = column.max;
+            if column_index >= min && column_index <= max {
+                return column.style.unwrap_or(0);
+            }
+        }
+        0
+    }
+
+    pub fn get_style(&self, row: i32, column: i32) -> i32 {
+        match self.sheet_data.get(&row) {
+            Some(column_data) => match column_data.get(&column) {
+                Some(cell) => cell.get_style(),
+                None => self.get_row_column_style(row, column),
+            },
+            None => self.get_row_column_style(row, column),
+        }
+    }
+
+    pub fn set_style(&mut self, style_index: i32) -> Result<(), String> {
+        self.cols = vec![Col {
+            min: 1,
+            max: constants::LAST_COLUMN,
+            width: constants::DEFAULT_COLUMN_WIDTH / constants::COLUMN_WIDTH_FACTOR,
+            custom_width: true,
+            style: Some(style_index),
+        }];
+        Ok(())
+    }
+
+    pub fn set_column_style(&mut self, column: i32, style_index: i32) -> Result<(), String> {
+        let cols = &mut self.cols;
+        let col = Col {
+            min: column,
+            max: column,
+            width: constants::DEFAULT_COLUMN_WIDTH / constants::COLUMN_WIDTH_FACTOR,
+            custom_width: true,
+            style: Some(style_index),
+        };
+        let mut index = 0;
+        let mut split = false;
+        for c in cols.iter_mut() {
+            let min = c.min;
+            let max = c.max;
+            if min <= column && column <= max {
+                if min == column && max == column {
+                    c.style = Some(style_index);
+                    return Ok(());
+                } else {
+                    // We need to split the result
+                    split = true;
+                    break;
+                }
+            }
+            if column < min {
+                // We passed, we should insert at index
+                break;
+            }
+            index += 1;
+        }
+        if split {
+            let min = cols[index].min;
+            let max = cols[index].max;
+            let pre = Col {
+                min,
+                max: column - 1,
+                width: cols[index].width,
+                custom_width: cols[index].custom_width,
+                style: cols[index].style,
+            };
+            let post = Col {
+                min: column + 1,
+                max,
+                width: cols[index].width,
+                custom_width: cols[index].custom_width,
+                style: cols[index].style,
+            };
+            cols.remove(index);
+            if column != max {
+                cols.insert(index, post);
+            }
+            cols.insert(index, col);
+            if column != min {
+                cols.insert(index, pre);
+            }
+        } else {
+            cols.insert(index, col);
+        }
+        Ok(())
+    }
+
+    pub fn set_row_style(&mut self, row: i32, style_index: i32) -> Result<(), String> {
+        for r in self.rows.iter_mut() {
+            if r.r == row {
+                r.s = style_index;
+                r.custom_format = true;
+                return Ok(());
+            }
+        }
+        self.rows.push(Row {
+            height: constants::DEFAULT_ROW_HEIGHT / constants::ROW_HEIGHT_FACTOR,
+            r: row,
+            custom_format: true,
+            custom_height: true,
+            s: style_index,
+            hidden: false,
+        });
+        Ok(())
+    }
+
+    pub fn set_cell_style(&mut self, row: i32, column: i32, style_index: i32) {
+        match self.cell_mut(row, column) {
+            Some(cell) => {
+                cell.set_style(style_index);
+            }
+            None => {
+                self.set_cell_empty_with_style(row, column, style_index);
+            }
+        }
+
+        // TODO: cleanup check if the old cell style is still in use
+    }
+
+    pub fn set_cell_with_formula(&mut self, row: i32, column: i32, index: i32, style: i32) {
+        let cell = Cell::new_formula(index, style);
+        self.update_cell(row, column, cell);
+    }
+
+    pub fn set_cell_with_number(&mut self, row: i32, column: i32, value: f64, style: i32) {
+        let cell = Cell::new_number(value, style);
+        self.update_cell(row, column, cell);
+    }
+
+    pub fn set_cell_with_string(&mut self, row: i32, column: i32, index: i32, style: i32) {
+        let cell = Cell::new_string(index, style);
+        self.update_cell(row, column, cell);
+    }
+
+    pub fn set_cell_with_boolean(&mut self, row: i32, column: i32, value: bool, style: i32) {
+        let cell = Cell::new_boolean(value, style);
+        self.update_cell(row, column, cell);
+    }
+
+    pub fn set_cell_with_error(&mut self, row: i32, column: i32, error: Error, style: i32) {
+        let cell = Cell::new_error(error, style);
+        self.update_cell(row, column, cell);
+    }
+
+    pub fn set_cell_empty(&mut self, row: i32, column: i32) {
+        let s = self.get_style(row, column);
+        let cell = Cell::EmptyCell { s };
+        self.update_cell(row, column, cell);
+    }
+
+    pub fn set_cell_empty_with_style(&mut self, row: i32, column: i32, style: i32) {
+        let cell = Cell::EmptyCell { s: style };
+        self.update_cell(row, column, cell);
+    }
+
+    pub fn set_frozen_rows(&mut self, frozen_rows: i32) -> Result<(), String> {
+        if frozen_rows < 0 {
+            return Err("Frozen rows cannot be negative".to_string());
+        } else if frozen_rows >= constants::LAST_ROW {
+            return Err("Too many rows".to_string());
+        }
+        self.frozen_rows = frozen_rows;
+        Ok(())
+    }
+
+    pub fn set_frozen_columns(&mut self, frozen_columns: i32) -> Result<(), String> {
+        if frozen_columns < 0 {
+            return Err("Frozen columns cannot be negative".to_string());
+        } else if frozen_columns >= constants::LAST_COLUMN {
+            return Err("Too many columns".to_string());
+        }
+        self.frozen_columns = frozen_columns;
+        Ok(())
+    }
+
+    /// Changes the height of a row.
+    ///   * If the row does not a have a style we add it.
+    ///   * If it has we modify the height and make sure it is applied.
+    /// Fails if column index is outside allowed range.
+    pub fn set_row_height(&mut self, row: i32, height: f64) -> Result<(), String> {
+        if !is_valid_row(row) {
+            return Err(format!("Row number '{row}' is not valid."));
+        }
+
+        let rows = &mut self.rows;
+        for r in rows.iter_mut() {
+            if r.r == row {
+                r.height = height / constants::ROW_HEIGHT_FACTOR;
+                r.custom_height = true;
+                return Ok(());
+            }
+        }
+        rows.push(Row {
+            height: height / constants::ROW_HEIGHT_FACTOR,
+            r: row,
+            custom_format: false,
+            custom_height: true,
+            s: 0,
+            hidden: false,
+        });
+        Ok(())
+    }
+    /// Changes the width of a column.
+    ///   * If the column does not a have a width we simply add it
+    ///   * If it has, it might be part of a range and we ned to split the range.
+    /// Fails if column index is outside allowed range.
+    pub fn set_column_width(&mut self, column: i32, width: f64) -> Result<(), String> {
+        if !is_valid_column_number(column) {
+            return Err(format!("Column number '{column}' is not valid."));
+        }
+        let cols = &mut self.cols;
+        let mut col = Col {
+            min: column,
+            max: column,
+            width: width / constants::COLUMN_WIDTH_FACTOR,
+            custom_width: true,
+            style: None,
+        };
+        let mut index = 0;
+        let mut split = false;
+        for c in cols.iter_mut() {
+            let min = c.min;
+            let max = c.max;
+            if min <= column && column <= max {
+                if min == column && max == column {
+                    c.width = width / constants::COLUMN_WIDTH_FACTOR;
+                    return Ok(());
+                } else {
+                    // We need to split the result
+                    split = true;
+                    break;
+                }
+            }
+            if column < min {
+                // We passed, we should insert at index
+                break;
+            }
+            index += 1;
+        }
+        if split {
+            let min = cols[index].min;
+            let max = cols[index].max;
+            let pre = Col {
+                min,
+                max: column - 1,
+                width: cols[index].width,
+                custom_width: cols[index].custom_width,
+                style: cols[index].style,
+            };
+            let post = Col {
+                min: column + 1,
+                max,
+                width: cols[index].width,
+                custom_width: cols[index].custom_width,
+                style: cols[index].style,
+            };
+            col.style = cols[index].style;
+            cols.remove(index);
+            if column != max {
+                cols.insert(index, post);
+            }
+            cols.insert(index, col);
+            if column != min {
+                cols.insert(index, pre);
+            }
+        } else {
+            cols.insert(index, col);
+        }
+        Ok(())
+    }
+
+    /// Return the width of a column in pixels
+    pub fn column_width(&self, column: i32) -> Result<f64, String> {
+        if !is_valid_column_number(column) {
+            return Err(format!("Column number '{column}' is not valid."));
+        }
+
+        let cols = &self.cols;
+        for col in cols {
+            let min = col.min;
+            let max = col.max;
+            if column >= min && column <= max {
+                if col.custom_width {
+                    return Ok(col.width * constants::COLUMN_WIDTH_FACTOR);
+                } else {
+                    break;
+                }
+            }
+        }
+        Ok(constants::DEFAULT_COLUMN_WIDTH)
+    }
+
+    // Returns non empty cells in a column
+    pub fn column_cell_references(&self, column: i32) -> Result<Vec<CellReferenceIndex>, String> {
+        let mut column_cell_references: Vec<CellReferenceIndex> = Vec::new();
+        if !is_valid_column_number(column) {
+            return Err(format!("Column number '{column}' is not valid."));
+        }
+
+        for row in self.sheet_data.keys() {
+            if self.cell(*row, column).is_some() {
+                column_cell_references.push(CellReferenceIndex {
+                    sheet: self.sheet_id,
+                    row: *row,
+                    column,
+                });
+            }
+        }
+        Ok(column_cell_references)
+    }
+
+    /// Returns the height of a row in pixels
+    pub fn row_height(&self, row: i32) -> Result<f64, String> {
+        if !is_valid_row(row) {
+            return Err(format!("Row number '{row}' is not valid."));
+        }
+
+        let rows = &self.rows;
+        for r in rows {
+            if r.r == row {
+                return Ok(r.height * constants::ROW_HEIGHT_FACTOR);
+            }
+        }
+        Ok(constants::DEFAULT_ROW_HEIGHT)
+    }
+
+    /// Returns non empty cells in a row
+    pub fn row_cell_references(&self, row: i32) -> Result<Vec<CellReferenceIndex>, String> {
+        let mut row_cell_references: Vec<CellReferenceIndex> = Vec::new();
+        if !is_valid_row(row) {
+            return Err(format!("Row number '{row}' is not valid."));
+        }
+
+        for (row_index, columns) in self.sheet_data.iter() {
+            if *row_index == row {
+                for column in columns.keys() {
+                    row_cell_references.push(CellReferenceIndex {
+                        sheet: self.sheet_id,
+                        row,
+                        column: *column,
+                    })
+                }
+            }
+        }
+        Ok(row_cell_references)
+    }
+
+    /// Returns non empty cells
+    pub fn cell_references(&self) -> Result<Vec<CellReferenceIndex>, String> {
+        let mut cell_references: Vec<CellReferenceIndex> = Vec::new();
+        for (row, columns) in self.sheet_data.iter() {
+            for column in columns.keys() {
+                cell_references.push(CellReferenceIndex {
+                    sheet: self.sheet_id,
+                    row: *row,
+                    column: *column,
+                })
+            }
+        }
+        Ok(cell_references)
+    }
+
+    /// Calculates dimension of the sheet. This function isn't cheap to calculate.
+    pub fn dimension(&self) -> WorksheetDimension {
+        // FIXME: It's probably better to just track the size as operations happen.
+        if self.sheet_data.is_empty() {
+            return WorksheetDimension {
+                min_row: 1,
+                max_row: 1,
+                min_column: 1,
+                max_column: 1,
+            };
+        }
+
+        let mut row_range: Option<(i32, i32)> = None;
+        let mut column_range: Option<(i32, i32)> = None;
+
+        for (row_index, columns) in &self.sheet_data {
+            row_range = if let Some((current_min, current_max)) = row_range {
+                Some((current_min.min(*row_index), current_max.max(*row_index)))
+            } else {
+                Some((*row_index, *row_index))
+            };
+
+            for column_index in columns.keys() {
+                column_range = if let Some((current_min, current_max)) = column_range {
+                    Some((
+                        current_min.min(*column_index),
+                        current_max.max(*column_index),
+                    ))
+                } else {
+                    Some((*column_index, *column_index))
+                }
+            }
+        }
+
+        let dimension = if let Some((min_row, max_row)) = row_range {
+            if let Some((min_column, max_column)) = column_range {
+                Some(WorksheetDimension {
+                    min_row,
+                    min_column,
+                    max_row,
+                    max_column,
+                })
+            } else {
+                None
+            }
+        } else {
+            None
+        };
+
+        dimension.unwrap_or(WorksheetDimension {
+            min_row: 1,
+            max_row: 1,
+            min_column: 1,
+            max_column: 1,
+        })
+    }
+
+    /// Returns true if cell is completely empty.
+    /// Cell with formula that evaluates to empty string is not considered empty.
+    pub fn is_empty_cell(&self, row: i32, column: i32) -> Result<bool, String> {
+        if !is_valid_column_number(column) || !is_valid_row(row) {
+            return Err("Row or column is outside valid range.".to_string());
+        }
+
+        let is_empty = if let Some(data_row) = self.sheet_data.get(&row) {
+            if let Some(cell) = data_row.get(&column) {
+                matches!(cell, Cell::EmptyCell { .. })
+            } else {
+                true
+            }
+        } else {
+            true
+        };
+
+        Ok(is_empty)
+    }
+
+    /// It provides convenient method for user navigation in the spreadsheet by jumping to edges.
+    /// Spreadsheet engines usually allow this method of navigation by using CTRL+arrows.
+    /// Behaviour summary:
+    /// - if starting cell is empty then find first non empty cell in given direction
+    /// - if starting cell is not empty, and neighbour in given direction is empty, then find
+    ///   first non empty cell in given direction
+    /// - if starting cell is not empty, and neighbour in given direction is also not empty, then
+    ///   find last non empty cell in given direction
+    pub fn navigate_to_edge_in_direction(
+        &self,
+        row: i32,
+        column: i32,
+        direction: NavigationDirection,
+    ) -> Result<(i32, i32), String> {
+        if !is_valid_column_number(column) || !is_valid_row(row) {
+            return Err("Row or column is outside valid range.".to_string());
+        }
+
+        let start_cell = (row, column);
+        let neighbour_cell = if let Some(cell) = step_in_direction(start_cell, direction) {
+            cell
+        } else {
+            return Ok((start_cell.0, start_cell.1));
+        };
+
+        if self.is_empty_cell(start_cell.0, start_cell.1)? {
+            // Find first non-empty cell or move to the end.
+            let found_cells = walk_in_direction(start_cell, direction, |(row, column)| {
+                Ok(!self.is_empty_cell(row, column)?)
+            })?;
+            Ok(match found_cells.found_cell {
+                Some(cell) => cell,
+                None => found_cells.previous_cell,
+            })
+        } else {
+            // Neighbour cell is empty     => find FIRST that is NOT empty
+            // Neighbour cell is not empty => find LAST  that is NOT empty in sequence
+            if self.is_empty_cell(neighbour_cell.0, neighbour_cell.1)? {
+                let found_cells = walk_in_direction(start_cell, direction, |(row, column)| {
+                    Ok(!self.is_empty_cell(row, column)?)
+                })?;
+                Ok(match found_cells.found_cell {
+                    Some(cell) => cell,
+                    None => found_cells.previous_cell,
+                })
+            } else {
+                let found_cells = walk_in_direction(start_cell, direction, |(row, column)| {
+                    self.is_empty_cell(row, column)
+                })?;
+                Ok(found_cells.previous_cell)
+            }
+        }
+    }
+}
+
+struct WalkFoundCells {
+    /// If cell is found, it contains coordinates of the cell, otherwise None
+    found_cell: Option<(i32, i32)>,
+    /// Previous cell in chain relative to `found_cell`.
+    /// If `found_cell` is None then it's last considered cell.
+    previous_cell: (i32, i32),
+}
+
+/// Walks in direction until condition is met or boundary reached.
+/// Returns tuple `(current_cell, previous_cell)`. `current_cell` is either None or passes predicate
+fn walk_in_direction<F>(
+    start_cell: (i32, i32),
+    direction: NavigationDirection,
+    predicate: F,
+) -> Result<WalkFoundCells, String>
+where
+    F: Fn((i32, i32)) -> Result<bool, String>,
+{
+    let mut previous_cell = start_cell;
+    let mut current_cell = step_in_direction(start_cell, direction);
+    while let Some(cell) = current_cell {
+        if !predicate((cell.0, cell.1))? {
+            previous_cell = cell;
+            current_cell = step_in_direction(cell, direction);
+        } else {
+            break;
+        }
+    }
+    Ok(WalkFoundCells {
+        found_cell: current_cell,
+        previous_cell,
+    })
+}
+
+/// Returns coordinate of cell in given direction from given cell.
+/// Returns `None` if steps over the edge.
+fn step_in_direction(
+    (row, column): (i32, i32),
+    direction: NavigationDirection,
+) -> Option<(i32, i32)> {
+    if (row == 1 && direction == NavigationDirection::Up)
+        || (row == LAST_ROW && direction == NavigationDirection::Down)
+        || (column == 1 && direction == NavigationDirection::Left)
+        || (column == LAST_COLUMN && direction == NavigationDirection::Right)
+    {
+        return None;
+    }
+
+    Some(match direction {
+        NavigationDirection::Left => (row, column - 1),
+        NavigationDirection::Right => (row, column + 1),
+        NavigationDirection::Up => (row - 1, column),
+        NavigationDirection::Down => (row + 1, column),
+    })
+}
+
\ No newline at end of file diff --git a/src/test/test.rs.html b/src/test/test.rs.html new file mode 100644 index 0000000..574687f --- /dev/null +++ b/src/test/test.rs.html @@ -0,0 +1,69 @@ +test.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+
//! Tests an Excel xlsx file.
+//! Returns a list of differences in json format.
+//! Saves an IronCalc version
+//! This is primary for QA internal testing and will be superseded by an official
+//! IronCalc CLI.
+//!
+//! Usage: test file.xlsx
+
+use std::path;
+
+use ironcalc::{compare::test_file, export::save_to_xlsx, import::load_model_from_xlsx};
+
+fn main() {
+    let args: Vec<_> = std::env::args().collect();
+    if args.len() != 2 {
+        panic!("Usage: {} <file.xlsx>", args[0]);
+    }
+    // first test the file
+    let file_name = &args[1];
+    println!("Testing file: {file_name}");
+    if let Err(message) = test_file(file_name) {
+        println!("{}", message);
+        panic!("Model was evaluated inconsistently with XLSX data.")
+    }
+
+    // save a copy my_xlsx_file.xlsx => my_xlsx_file.output.xlsx
+    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();
+    model.evaluate();
+    println!("Saving result as: {output_file_name}. Please open with Excel and test.");
+    save_to_xlsx(&model, output_file_name).unwrap();
+}
+
\ No newline at end of file diff --git a/static.files/COPYRIGHT-23e9bde6c69aea69.txt b/static.files/COPYRIGHT-23e9bde6c69aea69.txt new file mode 100644 index 0000000..1447df7 --- /dev/null +++ b/static.files/COPYRIGHT-23e9bde6c69aea69.txt @@ -0,0 +1,50 @@ +# REUSE-IgnoreStart + +These documentation pages include resources by third parties. This copyright +file applies only to those resources. The following third party resources are +included, and carry their own copyright notices and license terms: + +* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2): + + Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ + with Reserved Font Name Fira Sans. + + Copyright (c) 2014, Telefonica S.A. + + Licensed under the SIL Open Font License, Version 1.1. + See FiraSans-LICENSE.txt. + +* rustdoc.css, main.js, and playpen.js: + + Copyright 2015 The Rust Developers. + Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or + the MIT license (LICENSE-MIT.txt) at your option. + +* normalize.css: + + Copyright (c) Nicolas Gallagher and Jonathan Neal. + Licensed under the MIT license (see LICENSE-MIT.txt). + +* Source Code Pro (SourceCodePro-Regular.ttf.woff2, + SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2): + + Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), + with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark + of Adobe Systems Incorporated in the United States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceCodePro-LICENSE.txt. + +* Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2, + SourceSerif4-It.ttf.woff2): + + Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name + 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United + States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceSerif4-LICENSE.md. + +This copyright file is intended to be distributed with rustdoc output. + +# REUSE-IgnoreEnd diff --git a/static.files/FiraSans-LICENSE-db4b642586e02d97.txt b/static.files/FiraSans-LICENSE-db4b642586e02d97.txt new file mode 100644 index 0000000..d7e9c14 --- /dev/null +++ b/static.files/FiraSans-LICENSE-db4b642586e02d97.txt @@ -0,0 +1,98 @@ +// REUSE-IgnoreStart + +Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. +with Reserved Font Name < Fira >, + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 b/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 new file mode 100644 index 0000000..7a1e5fc Binary files /dev/null and b/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 differ diff --git a/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 b/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 new file mode 100644 index 0000000..e766e06 Binary files /dev/null and b/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 differ diff --git a/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt b/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/static.files/LICENSE-MIT-65090b722b3f6c56.txt b/static.files/LICENSE-MIT-65090b722b3f6c56.txt new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/static.files/LICENSE-MIT-65090b722b3f6c56.txt @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 b/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 new file mode 100644 index 0000000..1866ad4 Binary files /dev/null and b/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 differ diff --git a/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt b/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt new file mode 100644 index 0000000..4b3edc2 --- /dev/null +++ b/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt @@ -0,0 +1,103 @@ +// REUSE-IgnoreStart + +Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/), + +with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, +NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, +Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco, +NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic, +Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 b/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 new file mode 100644 index 0000000..462c34e Binary files /dev/null and b/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 differ diff --git a/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt b/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt new file mode 100644 index 0000000..0d2941e --- /dev/null +++ b/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt @@ -0,0 +1,97 @@ +// REUSE-IgnoreStart + +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 b/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 new file mode 100644 index 0000000..10b558e Binary files /dev/null and b/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 differ diff --git a/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 b/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 new file mode 100644 index 0000000..5ec64ee Binary files /dev/null and b/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 differ diff --git a/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 b/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 new file mode 100644 index 0000000..181a07f Binary files /dev/null and b/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 differ diff --git a/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 b/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 new file mode 100644 index 0000000..2ae08a7 Binary files /dev/null and b/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 differ diff --git a/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md b/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md new file mode 100644 index 0000000..175fa4f --- /dev/null +++ b/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md @@ -0,0 +1,98 @@ + + +Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. +Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + + diff --git a/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 b/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 new file mode 100644 index 0000000..0263fc3 Binary files /dev/null and b/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 differ diff --git a/static.files/clipboard-7571035ce49a181d.svg b/static.files/clipboard-7571035ce49a181d.svg new file mode 100644 index 0000000..8adbd99 --- /dev/null +++ b/static.files/clipboard-7571035ce49a181d.svg @@ -0,0 +1 @@ + diff --git a/static.files/favicon-16x16-8b506e7a72182f1c.png b/static.files/favicon-16x16-8b506e7a72182f1c.png new file mode 100644 index 0000000..ea4b45c Binary files /dev/null and b/static.files/favicon-16x16-8b506e7a72182f1c.png differ diff --git a/static.files/favicon-2c020d218678b618.svg b/static.files/favicon-2c020d218678b618.svg new file mode 100644 index 0000000..8b34b51 --- /dev/null +++ b/static.files/favicon-2c020d218678b618.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/static.files/favicon-32x32-422f7d1d52889060.png b/static.files/favicon-32x32-422f7d1d52889060.png new file mode 100644 index 0000000..69b8613 Binary files /dev/null and b/static.files/favicon-32x32-422f7d1d52889060.png differ diff --git a/static.files/main-9dd44ab47b99a0fb.js b/static.files/main-9dd44ab47b99a0fb.js new file mode 100644 index 0000000..cfb9a38 --- /dev/null +++ b/static.files/main-9dd44ab47b99a0fb.js @@ -0,0 +1,12 @@ +"use strict";window.RUSTDOC_TOOLTIP_HOVER_MS=300;window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS=450;function resourcePath(basename,extension){return getVar("root-path")+basename+getVar("resource-suffix")+extension}function hideMain(){addClass(document.getElementById(MAIN_ID),"hidden")}function showMain(){removeClass(document.getElementById(MAIN_ID),"hidden")}function elemIsInParent(elem,parent){while(elem&&elem!==document.body){if(elem===parent){return true}elem=elem.parentElement}return false}function blurHandler(event,parentElem,hideCallback){if(!elemIsInParent(document.activeElement,parentElem)&&!elemIsInParent(event.relatedTarget,parentElem)){hideCallback()}}window.rootPath=getVar("root-path");window.currentCrate=getVar("current-crate");function setMobileTopbar(){const mobileTopbar=document.querySelector(".mobile-topbar");const locationTitle=document.querySelector(".sidebar h2.location");if(mobileTopbar){const mobileTitle=document.createElement("h2");mobileTitle.className="location";if(hasClass(document.body,"crate")){mobileTitle.innerText=`Crate ${window.currentCrate}`}else if(locationTitle){mobileTitle.innerHTML=locationTitle.innerHTML}mobileTopbar.appendChild(mobileTitle)}}function getVirtualKey(ev){if("key"in ev&&typeof ev.key!=="undefined"){return ev.key}const c=ev.charCode||ev.keyCode;if(c===27){return"Escape"}return String.fromCharCode(c)}const MAIN_ID="main-content";const SETTINGS_BUTTON_ID="settings-menu";const ALTERNATIVE_DISPLAY_ID="alternative-display";const NOT_DISPLAYED_ID="not-displayed";const HELP_BUTTON_ID="help-button";function getSettingsButton(){return document.getElementById(SETTINGS_BUTTON_ID)}function getHelpButton(){return document.getElementById(HELP_BUTTON_ID)}function getNakedUrl(){return window.location.href.split("?")[0].split("#")[0]}function insertAfter(newNode,referenceNode){referenceNode.parentNode.insertBefore(newNode,referenceNode.nextSibling)}function getOrCreateSection(id,classes){let el=document.getElementById(id);if(!el){el=document.createElement("section");el.id=id;el.className=classes;insertAfter(el,document.getElementById(MAIN_ID))}return el}function getAlternativeDisplayElem(){return getOrCreateSection(ALTERNATIVE_DISPLAY_ID,"content hidden")}function getNotDisplayedElem(){return getOrCreateSection(NOT_DISPLAYED_ID,"hidden")}function switchDisplayedElement(elemToDisplay){const el=getAlternativeDisplayElem();if(el.children.length>0){getNotDisplayedElem().appendChild(el.firstElementChild)}if(elemToDisplay===null){addClass(el,"hidden");showMain();return}el.appendChild(elemToDisplay);hideMain();removeClass(el,"hidden")}function browserSupportsHistoryApi(){return window.history&&typeof window.history.pushState==="function"}function preLoadCss(cssUrl){const link=document.createElement("link");link.href=cssUrl;link.rel="preload";link.as="style";document.getElementsByTagName("head")[0].appendChild(link)}(function(){const isHelpPage=window.location.pathname.endsWith("/help.html");function loadScript(url){const script=document.createElement("script");script.src=url;document.head.append(script)}getSettingsButton().onclick=event=>{if(event.ctrlKey||event.altKey||event.metaKey){return}window.hideAllModals(false);addClass(getSettingsButton(),"rotate");event.preventDefault();loadScript(getVar("static-root-path")+getVar("settings-js"));setTimeout(()=>{const themes=getVar("themes").split(",");for(const theme of themes){if(theme!==""){preLoadCss(getVar("root-path")+theme+".css")}}},0)};window.searchState={loadingText:"Loading search results...",input:document.getElementsByClassName("search-input")[0],outputElement:()=>{let el=document.getElementById("search");if(!el){el=document.createElement("section");el.id="search";getNotDisplayedElem().appendChild(el)}return el},title:document.title,titleBeforeSearch:document.title,timeout:null,currentTab:0,focusedByTab:[null,null,null],clearInputTimeout:()=>{if(searchState.timeout!==null){clearTimeout(searchState.timeout);searchState.timeout=null}},isDisplayed:()=>searchState.outputElement().parentElement.id===ALTERNATIVE_DISPLAY_ID,focus:()=>{searchState.input.focus()},defocus:()=>{searchState.input.blur()},showResults:search=>{if(search===null||typeof search==="undefined"){search=searchState.outputElement()}switchDisplayedElement(search);searchState.mouseMovedAfterSearch=false;document.title=searchState.title},removeQueryParameters:()=>{document.title=searchState.titleBeforeSearch;if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.hash)}},hideResults:()=>{switchDisplayedElement(null);searchState.removeQueryParameters()},getQueryStringParams:()=>{const params={};window.location.search.substring(1).split("&").map(s=>{const pair=s.split("=");params[decodeURIComponent(pair[0])]=typeof pair[1]==="undefined"?null:decodeURIComponent(pair[1])});return params},setup:()=>{const search_input=searchState.input;if(!searchState.input){return}let searchLoaded=false;function loadSearch(){if(!searchLoaded){searchLoaded=true;loadScript(getVar("static-root-path")+getVar("search-js"));loadScript(resourcePath("search-index",".js"))}}search_input.addEventListener("focus",()=>{search_input.origPlaceholder=search_input.placeholder;search_input.placeholder="Type your search here.";loadSearch()});if(search_input.value!==""){loadSearch()}const params=searchState.getQueryStringParams();if(params.search!==undefined){searchState.setLoadingSearch();loadSearch()}},setLoadingSearch:()=>{const search=searchState.outputElement();search.innerHTML="

"+searchState.loadingText+"

";searchState.showResults(search)},};const toggleAllDocsId="toggle-all-docs";let savedHash="";function handleHashes(ev){if(ev!==null&&searchState.isDisplayed()&&ev.newURL){switchDisplayedElement(null);const hash=ev.newURL.slice(ev.newURL.indexOf("#")+1);if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.search+"#"+hash)}const elem=document.getElementById(hash);if(elem){elem.scrollIntoView()}}const pageId=window.location.hash.replace(/^#/,"");if(savedHash!==pageId){savedHash=pageId;if(pageId!==""){expandSection(pageId)}}if(savedHash.startsWith("impl-")){const splitAt=savedHash.indexOf("/");if(splitAt!==-1){const implId=savedHash.slice(0,splitAt);const assocId=savedHash.slice(splitAt+1);const implElem=document.getElementById(implId);if(implElem&&implElem.parentElement.tagName==="SUMMARY"&&implElem.parentElement.parentElement.tagName==="DETAILS"){onEachLazy(implElem.parentElement.parentElement.querySelectorAll(`[id^="${assocId}"]`),item=>{const numbered=/([^-]+)-([0-9]+)/.exec(item.id);if(item.id===assocId||(numbered&&numbered[1]===assocId)){openParentDetails(item);item.scrollIntoView();setTimeout(()=>{window.location.replace("#"+item.id)},0)}})}}}}function onHashChange(ev){hideSidebar();handleHashes(ev)}function openParentDetails(elem){while(elem){if(elem.tagName==="DETAILS"){elem.open=true}elem=elem.parentNode}}function expandSection(id){openParentDetails(document.getElementById(id))}function handleEscape(ev){searchState.clearInputTimeout();searchState.hideResults();ev.preventDefault();searchState.defocus();window.hideAllModals(true)}function handleShortcut(ev){const disableShortcuts=getSettingValue("disable-shortcuts")==="true";if(ev.ctrlKey||ev.altKey||ev.metaKey||disableShortcuts){return}if(document.activeElement.tagName==="INPUT"&&document.activeElement.type!=="checkbox"&&document.activeElement.type!=="radio"){switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break}}else{switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break;case"s":case"S":ev.preventDefault();searchState.focus();break;case"+":ev.preventDefault();expandAllDocs();break;case"-":ev.preventDefault();collapseAllDocs();break;case"?":showHelp();break;default:break}}}document.addEventListener("keypress",handleShortcut);document.addEventListener("keydown",handleShortcut);function addSidebarItems(){if(!window.SIDEBAR_ITEMS){return}const sidebar=document.getElementsByClassName("sidebar-elems")[0];function block(shortty,id,longty){const filtered=window.SIDEBAR_ITEMS[shortty];if(!filtered){return}const modpath=hasClass(document.body,"mod")?"../":"";const h3=document.createElement("h3");h3.innerHTML=`${longty}`;const ul=document.createElement("ul");ul.className="block "+shortty;for(const name of filtered){let path;if(shortty==="mod"){path=`${modpath}${name}/index.html`}else{path=`${modpath}${shortty}.${name}.html`}let current_page=document.location.href.toString();if(current_page.endsWith("/")){current_page+="index.html"}const link=document.createElement("a");link.href=path;if(link.href===current_page){link.className="current"}link.textContent=name;const li=document.createElement("li");li.appendChild(link);ul.appendChild(li)}sidebar.appendChild(h3);sidebar.appendChild(ul)}if(sidebar){block("primitive","primitives","Primitive Types");block("mod","modules","Modules");block("macro","macros","Macros");block("struct","structs","Structs");block("enum","enums","Enums");block("constant","constants","Constants");block("static","static","Statics");block("trait","traits","Traits");block("fn","functions","Functions");block("type","types","Type Aliases");block("union","unions","Unions");block("foreigntype","foreign-types","Foreign Types");block("keyword","keywords","Keywords");block("opaque","opaque-types","Opaque Types");block("attr","attributes","Attribute Macros");block("derive","derives","Derive Macros");block("traitalias","trait-aliases","Trait Aliases")}}window.register_implementors=imp=>{const implementors=document.getElementById("implementors-list");const synthetic_implementors=document.getElementById("synthetic-implementors-list");const inlined_types=new Set();const TEXT_IDX=0;const SYNTHETIC_IDX=1;const TYPES_IDX=2;if(synthetic_implementors){onEachLazy(synthetic_implementors.getElementsByClassName("impl"),el=>{const aliases=el.getAttribute("data-aliases");if(!aliases){return}aliases.split(",").forEach(alias=>{inlined_types.add(alias)})})}let currentNbImpls=implementors.getElementsByClassName("impl").length;const traitName=document.querySelector(".main-heading h1 > .trait").textContent;const baseIdName="impl-"+traitName+"-";const libs=Object.getOwnPropertyNames(imp);const script=document.querySelector("script[data-ignore-extern-crates]");const ignoreExternCrates=new Set((script?script.getAttribute("data-ignore-extern-crates"):"").split(","));for(const lib of libs){if(lib===window.currentCrate||ignoreExternCrates.has(lib)){continue}const structs=imp[lib];struct_loop:for(const struct of structs){const list=struct[SYNTHETIC_IDX]?synthetic_implementors:implementors;if(struct[SYNTHETIC_IDX]){for(const struct_type of struct[TYPES_IDX]){if(inlined_types.has(struct_type)){continue struct_loop}inlined_types.add(struct_type)}}const code=document.createElement("h3");code.innerHTML=struct[TEXT_IDX];addClass(code,"code-header");onEachLazy(code.getElementsByTagName("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});const currentId=baseIdName+currentNbImpls;const anchor=document.createElement("a");anchor.href="#"+currentId;addClass(anchor,"anchor");const display=document.createElement("div");display.id=currentId;addClass(display,"impl");display.appendChild(anchor);display.appendChild(code);list.appendChild(display);currentNbImpls+=1}}};if(window.pending_implementors){window.register_implementors(window.pending_implementors)}window.register_type_impls=imp=>{if(!imp||!imp[window.currentCrate]){return}window.pending_type_impls=null;const idMap=new Map();let implementations=document.getElementById("implementations-list");let trait_implementations=document.getElementById("trait-implementations-list");let trait_implementations_header=document.getElementById("trait-implementations");const script=document.querySelector("script[data-self-path]");const selfPath=script?script.getAttribute("data-self-path"):null;const mainContent=document.querySelector("#main-content");const sidebarSection=document.querySelector(".sidebar section");let methods=document.querySelector(".sidebar .block.method");let associatedTypes=document.querySelector(".sidebar .block.associatedtype");let associatedConstants=document.querySelector(".sidebar .block.associatedconstant");let sidebarTraitList=document.querySelector(".sidebar .block.trait-implementation");for(const impList of imp[window.currentCrate]){const types=impList.slice(2);const text=impList[0];const isTrait=impList[1]!==0;const traitName=impList[1];if(types.indexOf(selfPath)===-1){continue}let outputList=isTrait?trait_implementations:implementations;if(outputList===null){const outputListName=isTrait?"Trait Implementations":"Implementations";const outputListId=isTrait?"trait-implementations-list":"implementations-list";const outputListHeaderId=isTrait?"trait-implementations":"implementations";const outputListHeader=document.createElement("h2");outputListHeader.id=outputListHeaderId;outputListHeader.innerText=outputListName;outputList=document.createElement("div");outputList.id=outputListId;if(isTrait){const link=document.createElement("a");link.href=`#${outputListHeaderId}`;link.innerText="Trait Implementations";const h=document.createElement("h3");h.appendChild(link);trait_implementations=outputList;trait_implementations_header=outputListHeader;sidebarSection.appendChild(h);sidebarTraitList=document.createElement("ul");sidebarTraitList.className="block trait-implementation";sidebarSection.appendChild(sidebarTraitList);mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}else{implementations=outputList;if(trait_implementations){mainContent.insertBefore(outputListHeader,trait_implementations_header);mainContent.insertBefore(outputList,trait_implementations_header)}else{const mainContent=document.querySelector("#main-content");mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}}}const template=document.createElement("template");template.innerHTML=text;onEachLazy(template.content.querySelectorAll("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});onEachLazy(template.content.querySelectorAll("[id]"),el=>{let i=0;if(idMap.has(el.id)){i=idMap.get(el.id)}else if(document.getElementById(el.id)){i=1;while(document.getElementById(`${el.id}-${2 * i}`)){i=2*i}while(document.getElementById(`${el.id}-${i}`)){i+=1}}if(i!==0){const oldHref=`#${el.id}`;const newHref=`#${el.id}-${i}`;el.id=`${el.id}-${i}`;onEachLazy(template.content.querySelectorAll("a[href]"),link=>{if(link.getAttribute("href")===oldHref){link.href=newHref}})}idMap.set(el.id,i+1)});const templateAssocItems=template.content.querySelectorAll("section.tymethod, "+"section.method, section.associatedtype, section.associatedconstant");if(isTrait){const li=document.createElement("li");const a=document.createElement("a");a.href=`#${template.content.querySelector(".impl").id}`;a.textContent=traitName;li.appendChild(a);sidebarTraitList.append(li)}else{onEachLazy(templateAssocItems,item=>{let block=hasClass(item,"associatedtype")?associatedTypes:(hasClass(item,"associatedconstant")?associatedConstants:(methods));if(!block){const blockTitle=hasClass(item,"associatedtype")?"Associated Types":(hasClass(item,"associatedconstant")?"Associated Constants":("Methods"));const blockClass=hasClass(item,"associatedtype")?"associatedtype":(hasClass(item,"associatedconstant")?"associatedconstant":("method"));const blockHeader=document.createElement("h3");const blockLink=document.createElement("a");blockLink.href="#implementations";blockLink.innerText=blockTitle;blockHeader.appendChild(blockLink);block=document.createElement("ul");block.className=`block ${blockClass}`;const insertionReference=methods||sidebarTraitList;if(insertionReference){const insertionReferenceH=insertionReference.previousElementSibling;sidebarSection.insertBefore(blockHeader,insertionReferenceH);sidebarSection.insertBefore(block,insertionReferenceH)}else{sidebarSection.appendChild(blockHeader);sidebarSection.appendChild(block)}if(hasClass(item,"associatedtype")){associatedTypes=block}else if(hasClass(item,"associatedconstant")){associatedConstants=block}else{methods=block}}const li=document.createElement("li");const a=document.createElement("a");a.innerText=item.id.split("-")[0].split(".")[1];a.href=`#${item.id}`;li.appendChild(a);block.appendChild(li)})}outputList.appendChild(template.content)}for(const list of[methods,associatedTypes,associatedConstants,sidebarTraitList]){if(!list){continue}const newChildren=Array.prototype.slice.call(list.children);newChildren.sort((a,b)=>{const aI=a.innerText;const bI=b.innerText;return aIbI?1:0});list.replaceChildren(...newChildren)}};if(window.pending_type_impls){window.register_type_impls(window.pending_type_impls)}function addSidebarCrates(){if(!window.ALL_CRATES){return}const sidebarElems=document.getElementsByClassName("sidebar-elems")[0];if(!sidebarElems){return}const h3=document.createElement("h3");h3.innerHTML="Crates";const ul=document.createElement("ul");ul.className="block crate";for(const crate of window.ALL_CRATES){const link=document.createElement("a");link.href=window.rootPath+crate+"/index.html";if(window.rootPath!=="./"&&crate===window.currentCrate){link.className="current"}link.textContent=crate;const li=document.createElement("li");li.appendChild(link);ul.appendChild(li)}sidebarElems.appendChild(h3);sidebarElems.appendChild(ul)}function expandAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);removeClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hasClass(e,"type-contents-toggle")&&!hasClass(e,"more-examples-toggle")){e.open=true}});innerToggle.title="collapse all docs";innerToggle.children[0].innerText="\u2212"}function collapseAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);addClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(e.parentNode.id!=="implementations-list"||(!hasClass(e,"implementors-toggle")&&!hasClass(e,"type-contents-toggle"))){e.open=false}});innerToggle.title="expand all docs";innerToggle.children[0].innerText="+"}function toggleAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);if(!innerToggle){return}if(hasClass(innerToggle,"will-expand")){expandAllDocs()}else{collapseAllDocs()}}(function(){const toggles=document.getElementById(toggleAllDocsId);if(toggles){toggles.onclick=toggleAllDocs}const hideMethodDocs=getSettingValue("auto-hide-method-docs")==="true";const hideImplementations=getSettingValue("auto-hide-trait-implementations")==="true";const hideLargeItemContents=getSettingValue("auto-hide-large-items")!=="false";function setImplementorsTogglesOpen(id,open){const list=document.getElementById(id);if(list!==null){onEachLazy(list.getElementsByClassName("implementors-toggle"),e=>{e.open=open})}}if(hideImplementations){setImplementorsTogglesOpen("trait-implementations-list",false);setImplementorsTogglesOpen("blanket-implementations-list",false)}onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hideLargeItemContents&&hasClass(e,"type-contents-toggle")){e.open=true}if(hideMethodDocs&&hasClass(e,"method-toggle")){e.open=false}})}());window.rustdoc_add_line_numbers_to_examples=()=>{onEachLazy(document.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");if(line_numbers.length>0){return}const count=x.textContent.split("\n").length;const elems=[];for(let i=0;i{onEachLazy(document.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");for(const node of line_numbers){parent.removeChild(node)}})};if(getSettingValue("line-numbers")==="true"){window.rustdoc_add_line_numbers_to_examples()}function showSidebar(){window.hideAllModals(false);const sidebar=document.getElementsByClassName("sidebar")[0];addClass(sidebar,"shown")}function hideSidebar(){const sidebar=document.getElementsByClassName("sidebar")[0];removeClass(sidebar,"shown")}window.addEventListener("resize",()=>{if(window.CURRENT_TOOLTIP_ELEMENT){const base=window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE;const force_visible=base.TOOLTIP_FORCE_VISIBLE;hideTooltip(false);if(force_visible){showTooltip(base);base.TOOLTIP_FORCE_VISIBLE=true}}});const mainElem=document.getElementById(MAIN_ID);if(mainElem){mainElem.addEventListener("click",hideSidebar)}onEachLazy(document.querySelectorAll("a[href^='#']"),el=>{el.addEventListener("click",()=>{expandSection(el.hash.slice(1));hideSidebar()})});onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"),el=>{el.addEventListener("click",e=>{if(e.target.tagName!=="SUMMARY"&&e.target.tagName!=="A"){e.preventDefault()}})});function showTooltip(e){const notable_ty=e.getAttribute("data-notable-ty");if(!window.NOTABLE_TRAITS&¬able_ty){const data=document.getElementById("notable-traits-data");if(data){window.NOTABLE_TRAITS=JSON.parse(data.innerText)}else{throw new Error("showTooltip() called with notable without any notable traits!")}}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE===e){clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);return}window.hideAllModals(false);const wrapper=document.createElement("div");if(notable_ty){wrapper.innerHTML="
"+window.NOTABLE_TRAITS[notable_ty]+"
"}else{if(e.getAttribute("title")!==null){e.setAttribute("data-title",e.getAttribute("title"));e.removeAttribute("title")}if(e.getAttribute("data-title")!==null){const titleContent=document.createElement("div");titleContent.className="content";titleContent.appendChild(document.createTextNode(e.getAttribute("data-title")));wrapper.appendChild(titleContent)}}wrapper.className="tooltip popover";const focusCatcher=document.createElement("div");focusCatcher.setAttribute("tabindex","0");focusCatcher.onfocus=hideTooltip;wrapper.appendChild(focusCatcher);const pos=e.getBoundingClientRect();wrapper.style.top=(pos.top+window.scrollY+pos.height)+"px";wrapper.style.left=0;wrapper.style.right="auto";wrapper.style.visibility="hidden";const body=document.getElementsByTagName("body")[0];body.appendChild(wrapper);const wrapperPos=wrapper.getBoundingClientRect();const finalPos=pos.left+window.scrollX-wrapperPos.width+24;if(finalPos>0){wrapper.style.left=finalPos+"px"}else{wrapper.style.setProperty("--popover-arrow-offset",(wrapperPos.right-pos.right+4)+"px")}wrapper.style.visibility="";window.CURRENT_TOOLTIP_ELEMENT=wrapper;window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE=e;clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);wrapper.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}clearTooltipHoverTimeout(e)};wrapper.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&!elemIsInParent(ev.relatedTarget,e)){setTooltipHoverTimeout(e,false);addClass(wrapper,"fade-out")}}}function setTooltipHoverTimeout(element,show){clearTooltipHoverTimeout(element);if(!show&&!window.CURRENT_TOOLTIP_ELEMENT){return}if(show&&window.CURRENT_TOOLTIP_ELEMENT){return}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE!==element){return}element.TOOLTIP_HOVER_TIMEOUT=setTimeout(()=>{if(show){showTooltip(element)}else if(!element.TOOLTIP_FORCE_VISIBLE){hideTooltip(false)}},show?window.RUSTDOC_TOOLTIP_HOVER_MS:window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS)}function clearTooltipHoverTimeout(element){if(element.TOOLTIP_HOVER_TIMEOUT!==undefined){removeClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out");clearTimeout(element.TOOLTIP_HOVER_TIMEOUT);delete element.TOOLTIP_HOVER_TIMEOUT}}function tooltipBlurHandler(event){if(window.CURRENT_TOOLTIP_ELEMENT&&!elemIsInParent(document.activeElement,window.CURRENT_TOOLTIP_ELEMENT)&&!elemIsInParent(event.relatedTarget,window.CURRENT_TOOLTIP_ELEMENT)&&!elemIsInParent(document.activeElement,window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE)&&!elemIsInParent(event.relatedTarget,window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE)){setTimeout(()=>hideTooltip(false),0)}}function hideTooltip(focus){if(window.CURRENT_TOOLTIP_ELEMENT){if(window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE){if(focus){window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus()}window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE=false}const body=document.getElementsByTagName("body")[0];body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);window.CURRENT_TOOLTIP_ELEMENT=null}}onEachLazy(document.getElementsByClassName("tooltip"),e=>{e.onclick=()=>{e.TOOLTIP_FORCE_VISIBLE=e.TOOLTIP_FORCE_VISIBLE?false:true;if(window.CURRENT_TOOLTIP_ELEMENT&&!e.TOOLTIP_FORCE_VISIBLE){hideTooltip(true)}else{showTooltip(e);window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex","0");window.CURRENT_TOOLTIP_ELEMENT.focus();window.CURRENT_TOOLTIP_ELEMENT.onblur=tooltipBlurHandler}return false};e.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointermove=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&!elemIsInParent(ev.relatedTarget,window.CURRENT_TOOLTIP_ELEMENT)){setTooltipHoverTimeout(e,false);addClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out")}}});const sidebar_menu_toggle=document.getElementsByClassName("sidebar-menu-toggle")[0];if(sidebar_menu_toggle){sidebar_menu_toggle.addEventListener("click",()=>{const sidebar=document.getElementsByClassName("sidebar")[0];if(!hasClass(sidebar,"shown")){showSidebar()}else{hideSidebar()}})}function helpBlurHandler(event){blurHandler(event,getHelpButton(),window.hidePopoverMenus)}function buildHelpMenu(){const book_info=document.createElement("span");const channel=getVar("channel");book_info.className="top";book_info.innerHTML=`You can find more information in \ +the rustdoc book.`;const shortcuts=[["?","Show this help dialog"],["S","Focus the search field"],["↑","Move up in search results"],["↓","Move down in search results"],["← / →","Switch result tab (when results focused)"],["⏎","Go to active search result"],["+","Expand all sections"],["-","Collapse all sections"],].map(x=>"
"+x[0].split(" ").map((y,index)=>((index&1)===0?""+y+"":" "+y+" ")).join("")+"
"+x[1]+"
").join("");const div_shortcuts=document.createElement("div");addClass(div_shortcuts,"shortcuts");div_shortcuts.innerHTML="

Keyboard Shortcuts

"+shortcuts+"
";const infos=[`For a full list of all search features, take a look here.`,"Prefix searches with a type followed by a colon (e.g., fn:) to \ + restrict the search to a given item kind.","Accepted kinds are: fn, mod, struct, \ + enum, trait, type, macro, \ + and const.","Search functions by type signature (e.g., vec -> usize or \ + -> vec or String, enum:Cow -> bool)","You can look for items with an exact name by putting double quotes around \ + your request: \"string\"","Look for functions that accept or return \ + slices and \ + arrays by writing \ + square brackets (e.g., -> [u8] or [] -> Option)","Look for items inside another one by searching for a path: vec::Vec",].map(x=>"

"+x+"

").join("");const div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="

Search Tricks

"+infos;const rustdoc_version=document.createElement("span");rustdoc_version.className="bottom";const rustdoc_version_code=document.createElement("code");rustdoc_version_code.innerText="rustdoc "+getVar("rustdoc-version");rustdoc_version.appendChild(rustdoc_version_code);const container=document.createElement("div");if(!isHelpPage){container.className="popover"}container.id="help";container.style.display="none";const side_by_side=document.createElement("div");side_by_side.className="side-by-side";side_by_side.appendChild(div_shortcuts);side_by_side.appendChild(div_infos);container.appendChild(book_info);container.appendChild(side_by_side);container.appendChild(rustdoc_version);if(isHelpPage){const help_section=document.createElement("section");help_section.appendChild(container);document.getElementById("main-content").appendChild(help_section);container.style.display="block"}else{const help_button=getHelpButton();help_button.appendChild(container);container.onblur=helpBlurHandler;help_button.onblur=helpBlurHandler;help_button.children[0].onblur=helpBlurHandler}return container}window.hideAllModals=switchFocus=>{hideSidebar();window.hidePopoverMenus();hideTooltip(switchFocus)};window.hidePopoverMenus=()=>{onEachLazy(document.querySelectorAll(".search-form .popover"),elem=>{elem.style.display="none"})};function getHelpMenu(buildNeeded){let menu=getHelpButton().querySelector(".popover");if(!menu&&buildNeeded){menu=buildHelpMenu()}return menu}function showHelp(){getHelpButton().querySelector("a").focus();const menu=getHelpMenu(true);if(menu.style.display==="none"){window.hideAllModals();menu.style.display=""}}if(isHelpPage){showHelp();document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault()})}else{document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault();const menu=getHelpMenu(true);const shouldShowHelp=menu.style.display==="none";if(shouldShowHelp){showHelp()}else{window.hidePopoverMenus()}})}setMobileTopbar();addSidebarItems();addSidebarCrates();onHashChange(null);window.addEventListener("hashchange",onHashChange);searchState.setup()}());(function(){let reset_button_timeout=null;const but=document.getElementById("copy-path");if(!but){return}but.onclick=()=>{const parent=but.parentElement;const path=[];onEach(parent.childNodes,child=>{if(child.tagName==="A"){path.push(child.textContent)}});const el=document.createElement("textarea");el.value=path.join("::");el.setAttribute("readonly","");el.style.position="absolute";el.style.left="-9999px";document.body.appendChild(el);el.select();document.execCommand("copy");document.body.removeChild(el);but.children[0].style.display="none";let tmp;if(but.childNodes.length<2){tmp=document.createTextNode("✓");but.appendChild(tmp)}else{onEachLazy(but.childNodes,e=>{if(e.nodeType===Node.TEXT_NODE){tmp=e;return true}});tmp.textContent="✓"}if(reset_button_timeout!==null){window.clearTimeout(reset_button_timeout)}function reset_button(){tmp.textContent="";reset_button_timeout=null;but.children[0].style.display=""}reset_button_timeout=window.setTimeout(reset_button,1000)}}()) \ No newline at end of file diff --git a/static.files/normalize-76eba96aa4d2e634.css b/static.files/normalize-76eba96aa4d2e634.css new file mode 100644 index 0000000..469959f --- /dev/null +++ b/static.files/normalize-76eba96aa4d2e634.css @@ -0,0 +1,2 @@ + /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none} \ No newline at end of file diff --git a/static.files/noscript-5d8b3c7633ad77ba.css b/static.files/noscript-5d8b3c7633ad77ba.css new file mode 100644 index 0000000..8c63ef0 --- /dev/null +++ b/static.files/noscript-5d8b3c7633ad77ba.css @@ -0,0 +1 @@ + #main-content .attributes{margin-left:0 !important;}#copy-path{display:none;}nav.sub{display:none;}.src .sidebar{display:none;}.notable-traits{display:none;}:root{--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--test-arrow-color:#f5f5f5;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#f5f5f5;--test-arrow-hover-background-color:rgb(78,139,202);--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);}@media (prefers-color-scheme:dark){:root{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--test-arrow-color:#dedede;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#dedede;--test-arrow-hover-background-color:#4e8bca;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);}} \ No newline at end of file diff --git a/static.files/rust-logo-151179464ae7ed46.svg b/static.files/rust-logo-151179464ae7ed46.svg new file mode 100644 index 0000000..62424d8 --- /dev/null +++ b/static.files/rust-logo-151179464ae7ed46.svg @@ -0,0 +1,61 @@ + + + diff --git a/static.files/rustdoc-9ee3a5e31a2afa3e.css b/static.files/rustdoc-9ee3a5e31a2afa3e.css new file mode 100644 index 0000000..8749d0e --- /dev/null +++ b/static.files/rustdoc-9ee3a5e31a2afa3e.css @@ -0,0 +1,10 @@ + :root{--nav-sub-mobile-padding:8px;--search-typename-width:6.75rem;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:400;src:local('Fira Sans'),url("FiraSans-Regular-018c141bf0843ffd.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:500;src:local('Fira Sans Medium'),url("FiraSans-Medium-8f9a781e4970d388.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:400;src:local('Source Serif 4'),url("SourceSerif4-Regular-46f98efaafac5295.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:italic;font-weight:400;src:local('Source Serif 4 Italic'),url("SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:700;src:local('Source Serif 4 Bold'),url("SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:400;src:url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:italic;font-weight:400;src:url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:600;src:url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'NanumBarunGothic';src:url("NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2") format("woff2");font-display:swap;unicode-range:U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF;}*{box-sizing:border-box;}body{font:1rem/1.5 "Source Serif 4",NanumBarunGothic,serif;margin:0;position:relative;overflow-wrap:break-word;overflow-wrap:anywhere;font-feature-settings:"kern","liga";background-color:var(--main-background-color);color:var(--main-color);}h1{font-size:1.5rem;}h2{font-size:1.375rem;}h3{font-size:1.25rem;}h1,h2,h3,h4,h5,h6{font-weight:500;}h1,h2,h3,h4{margin:25px 0 15px 0;padding-bottom:6px;}.docblock h3,.docblock h4,h5,h6{margin:15px 0 5px 0;}.docblock>h2:first-child,.docblock>h3:first-child,.docblock>h4:first-child,.docblock>h5:first-child,.docblock>h6:first-child{margin-top:0;}.main-heading h1{margin:0;padding:0;flex-grow:1;overflow-wrap:break-word;overflow-wrap:anywhere;}.main-heading{display:flex;flex-wrap:wrap;padding-bottom:6px;margin-bottom:15px;}.content h2,.top-doc .docblock>h3,.top-doc .docblock>h4{border-bottom:1px solid var(--headings-border-bottom-color);}h1,h2{line-height:1.25;padding-top:3px;padding-bottom:9px;}h3.code-header{font-size:1.125rem;}h4.code-header{font-size:1rem;}.code-header{font-weight:600;margin:0;padding:0;white-space:pre-wrap;}#crate-search,h1,h2,h3,h4,h5,h6,.sidebar,.mobile-topbar,.search-input,.search-results .result-name,.item-name>a,.out-of-band,span.since,a.src,#help-button>a,summary.hideme,.scraped-example-list,ul.all-items{font-family:"Fira Sans",Arial,NanumBarunGothic,sans-serif;}#toggle-all-docs,a.anchor,.small-section-header a,#src-sidebar a,.rust a,.sidebar h2 a,.sidebar h3 a,.mobile-topbar h2 a,h1 a,.search-results a,.stab,.result-name i{color:var(--main-color);}span.enum,a.enum,span.struct,a.struct,span.union,a.union,span.primitive,a.primitive,span.type,a.type,span.foreigntype,a.foreigntype{color:var(--type-link-color);}span.trait,a.trait,span.traitalias,a.traitalias{color:var(--trait-link-color);}span.associatedtype,a.associatedtype,span.constant,a.constant,span.static,a.static{color:var(--assoc-item-link-color);}span.fn,a.fn,span.method,a.method,span.tymethod,a.tymethod{color:var(--function-link-color);}span.attr,a.attr,span.derive,a.derive,span.macro,a.macro{color:var(--macro-link-color);}span.mod,a.mod{color:var(--mod-link-color);}span.keyword,a.keyword{color:var(--keyword-link-color);}a{color:var(--link-color);text-decoration:none;}ol,ul{padding-left:24px;}ul ul,ol ul,ul ol,ol ol{margin-bottom:.625em;}p,.docblock>.warning{margin:0 0 .75em 0;}p:last-child,.docblock>.warning:last-child{margin:0;}button{padding:1px 6px;cursor:pointer;}button#toggle-all-docs{padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.rustdoc{display:flex;flex-direction:row;flex-wrap:nowrap;}main{position:relative;flex-grow:1;padding:10px 15px 40px 45px;min-width:0;}.src main{padding:15px;}.width-limiter{max-width:960px;margin-right:auto;}details:not(.toggle) summary{margin-bottom:.6em;}code,pre,a.test-arrow,.code-header{font-family:"Source Code Pro",monospace;}.docblock code,.docblock-short code{border-radius:3px;padding:0 0.125em;}.docblock pre code,.docblock-short pre code{padding:0;}pre{padding:14px;line-height:1.5;}pre.item-decl{overflow-x:auto;}.item-decl .type-contents-toggle{contain:initial;}.src .content pre{padding:20px;}.rustdoc.src .example-wrap pre.src-line-numbers{padding:20px 0 20px 4px;}img{max-width:100%;}.sub-logo-container,.logo-container{line-height:0;display:block;}.sub-logo-container{margin-right:32px;}.sub-logo-container>img{height:60px;width:60px;object-fit:contain;}.rust-logo{filter:var(--rust-logo-filter);}.sidebar{font-size:0.875rem;flex:0 0 200px;overflow-y:scroll;overscroll-behavior:contain;position:sticky;height:100vh;top:0;left:0;}.rustdoc.src .sidebar{flex-basis:50px;border-right:1px solid;overflow-x:hidden;overflow-y:hidden;z-index:1;}.sidebar,.mobile-topbar,.sidebar-menu-toggle,#src-sidebar-toggle,#src-sidebar{background-color:var(--sidebar-background-color);}#src-sidebar-toggle>button:hover,#src-sidebar-toggle>button:focus{background-color:var(--sidebar-background-color-hover);}.src .sidebar>*:not(#src-sidebar-toggle){visibility:hidden;}.src-sidebar-expanded .src .sidebar{overflow-y:auto;flex-basis:300px;}.src-sidebar-expanded .src .sidebar>*:not(#src-sidebar-toggle){visibility:visible;}#all-types{margin-top:1em;}*{scrollbar-width:initial;scrollbar-color:var(--scrollbar-color);}.sidebar{scrollbar-width:thin;scrollbar-color:var(--scrollbar-color);}::-webkit-scrollbar{width:12px;}.sidebar::-webkit-scrollbar{width:8px;}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0;background-color:var(--scrollbar-track-background-color);}.sidebar::-webkit-scrollbar-track{background-color:var(--scrollbar-track-background-color);}::-webkit-scrollbar-thumb,.sidebar::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb-background-color);}.hidden{display:none !important;}.logo-container>img{height:48px;width:48px;}ul.block,.block li{padding:0;margin:0;list-style:none;}.sidebar-elems a,.sidebar>h2 a{display:block;padding:0.25rem;margin-left:-0.25rem;}.sidebar h2{overflow-wrap:anywhere;padding:0;margin:0.7rem 0;}.sidebar h3{font-size:1.125rem;padding:0;margin:0;}.sidebar-elems,.sidebar>.version,.sidebar>h2{padding-left:24px;}.sidebar a{color:var(--sidebar-link-color);}.sidebar .current,.sidebar .current a,.sidebar-crate a.logo-container:hover+h2 a,.sidebar a:hover:not(.logo-container){background-color:var(--sidebar-current-link-background-color);}.sidebar-elems .block{margin-bottom:2em;}.sidebar-elems .block li a{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}.sidebar-crate{display:flex;align-items:center;justify-content:center;margin:14px 32px 1rem;row-gap:10px;column-gap:32px;flex-wrap:wrap;}.sidebar-crate h2{flex-grow:1;margin:0 -8px;align-self:start;}.sidebar-crate .logo-container{margin:0 -16px 0 -16px;text-align:center;}.sidebar-crate h2 a{display:block;margin:0 calc(-24px + 0.25rem) 0 -0.5rem;padding:calc((16px - 0.57rem ) / 2 ) 0.25rem;padding-left:0.5rem;}.sidebar-crate h2 .version{display:block;font-weight:normal;font-size:1rem;overflow-wrap:break-word;margin-top:calc((-16px + 0.57rem ) / 2 );}.sidebar-crate+.version{margin-top:-1rem;margin-bottom:1rem;}.mobile-topbar{display:none;}.rustdoc .example-wrap{display:flex;position:relative;margin-bottom:10px;}.rustdoc .example-wrap:last-child{margin-bottom:0px;}.rustdoc .example-wrap pre{margin:0;flex-grow:1;}.rustdoc:not(.src) .example-wrap pre{overflow:auto hidden;}.rustdoc .example-wrap pre.example-line-numbers,.rustdoc .example-wrap pre.src-line-numbers{flex-grow:0;min-width:fit-content;overflow:initial;text-align:right;-webkit-user-select:none;user-select:none;padding:14px 8px;color:var(--src-line-numbers-span-color);}.rustdoc .example-wrap pre.src-line-numbers{padding:14px 0;}.src-line-numbers a,.src-line-numbers span{color:var(--src-line-numbers-span-color);padding:0 8px;}.src-line-numbers :target{background-color:transparent;border-right:none;padding:0 8px;}.src-line-numbers .line-highlighted{background-color:var(--src-line-number-highlighted-background-color);}.search-loading{text-align:center;}.docblock-short{overflow-wrap:break-word;overflow-wrap:anywhere;}.docblock :not(pre)>code,.docblock-short code{white-space:pre-wrap;}.top-doc .docblock h2{font-size:1.375rem;}.top-doc .docblock h3{font-size:1.25rem;}.top-doc .docblock h4,.top-doc .docblock h5{font-size:1.125rem;}.top-doc .docblock h6{font-size:1rem;}.docblock h5{font-size:1rem;}.docblock h6{font-size:0.875rem;}.docblock{margin-left:24px;position:relative;}.docblock>:not(.more-examples-toggle):not(.example-wrap){max-width:100%;overflow-x:auto;}.out-of-band{flex-grow:0;font-size:1.125rem;}.docblock code,.docblock-short code,pre,.rustdoc.src .example-wrap{background-color:var(--code-block-background-color);}#main-content{position:relative;}.docblock table{margin:.5em 0;border-collapse:collapse;}.docblock table td,.docblock table th{padding:.5em;border:1px solid var(--border-color);}.docblock table tbody tr:nth-child(2n){background:var(--table-alt-row-background-color);}.method .where,.fn .where,.where.fmt-newline{display:block;white-space:pre-wrap;font-size:0.875rem;}.item-info{display:block;margin-left:24px;}.item-info code{font-size:0.875rem;}#main-content>.item-info{margin-left:0;}nav.sub{flex-grow:1;flex-flow:row nowrap;margin:4px 0 25px 0;display:flex;align-items:center;}.search-form{position:relative;display:flex;height:34px;flex-grow:1;}.src nav.sub{margin:0 0 15px 0;}.small-section-header{display:block;position:relative;}.small-section-header:hover>.anchor,.impl:hover>.anchor,.trait-impl:hover>.anchor,.variant:hover>.anchor{display:initial;}.anchor{display:none;position:absolute;left:-0.5em;background:none !important;}.anchor.field{left:-5px;}.small-section-header>.anchor{left:-15px;padding-right:8px;}h2.small-section-header>.anchor{padding-right:6px;}.main-heading a:hover,.example-wrap .rust a:hover,.all-items a:hover,.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,.docblock-short a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,.item-info a{text-decoration:underline;}.crate.block a.current{font-weight:500;}table,.item-table{overflow-wrap:break-word;}.item-table{display:table;padding:0;margin:0;}.item-table>li{display:table-row;}.item-table>li>div{display:table-cell;}.item-table>li>.item-name{padding-right:1.25rem;}.search-results-title{margin-top:0;white-space:nowrap;display:flex;align-items:baseline;}#crate-search-div{position:relative;min-width:5em;}#crate-search{min-width:115px;padding:0 23px 0 4px;max-width:100%;text-overflow:ellipsis;border:1px solid var(--border-color);border-radius:4px;outline:none;cursor:pointer;-moz-appearance:none;-webkit-appearance:none;text-indent:0.01px;background-color:var(--main-background-color);color:inherit;line-height:1.5;font-weight:500;}#crate-search:hover,#crate-search:focus{border-color:var(--crate-search-hover-border);}#crate-search-div::after{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0;content:"";background-repeat:no-repeat;background-size:20px;background-position:calc(100% - 2px) 56%;background-image:url('data:image/svg+xml, \ + ');filter:var(--crate-search-div-filter);}#crate-search-div:hover::after,#crate-search-div:focus-within::after{filter:var(--crate-search-div-hover-filter);}#crate-search>option{font-size:1rem;}.search-input{-webkit-appearance:none;outline:none;border:1px solid var(--border-color);border-radius:2px;padding:8px;font-size:1rem;flex-grow:1;background-color:var(--button-background-color);color:var(--search-color);}.search-input:focus{border-color:var(--search-input-focused-border-color);}.search-results{display:none;}.search-results.active{display:block;}.search-results>a{display:flex;margin-left:2px;margin-right:2px;border-bottom:1px solid var(--search-result-border-color);gap:1em;}.search-results>a>div.desc{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex:2;}.search-results a:hover,.search-results a:focus{background-color:var(--search-result-link-focus-background-color);}.search-results .result-name{display:flex;align-items:center;justify-content:start;flex:3;}.search-results .result-name .alias{color:var(--search-results-alias-color);}.search-results .result-name .grey{color:var(--search-results-grey-color);}.search-results .result-name .typename{color:var(--search-results-grey-color);font-size:0.875rem;width:var(--search-typename-width);}.search-results .result-name .path{word-break:break-all;max-width:calc(100% - var(--search-typename-width));display:inline-block;}.search-results .result-name .path>*{display:inline;}.popover{position:absolute;top:100%;right:0;z-index:2;margin-top:7px;border-radius:3px;border:1px solid var(--border-color);background-color:var(--main-background-color);color:var(--main-color);--popover-arrow-offset:11px;}.popover::before{content:'';position:absolute;right:var(--popover-arrow-offset);border:solid var(--border-color);border-width:1px 1px 0 0;background-color:var(--main-background-color);padding:4px;transform:rotate(-45deg);top:-5px;}.setting-line{margin:1.2em 0.6em;}.setting-radio input,.setting-check input{margin-right:0.3em;height:1.2rem;width:1.2rem;border:2px solid var(--settings-input-border-color);outline:none;-webkit-appearance:none;cursor:pointer;}.setting-radio input{border-radius:50%;}.setting-radio span,.setting-check span{padding-bottom:1px;}.setting-radio{margin-top:0.1em;margin-bottom:0.1em;min-width:3.8em;padding:0.3em;display:inline-flex;align-items:center;cursor:pointer;}.setting-radio+.setting-radio{margin-left:0.5em;}.setting-check{margin-right:20px;display:flex;align-items:center;cursor:pointer;}.setting-radio input:checked{box-shadow:inset 0 0 0 3px var(--main-background-color);background-color:var(--settings-input-color);}.setting-check input:checked{background-color:var(--settings-input-color);border-width:1px;content:url('data:image/svg+xml,\ + \ + ');}.setting-radio input:focus,.setting-check input:focus{box-shadow:0 0 1px 1px var(--settings-input-color);}.setting-radio input:checked:focus{box-shadow:inset 0 0 0 3px var(--main-background-color),0 0 2px 2px var(--settings-input-color);}.setting-radio input:hover,.setting-check input:hover{border-color:var(--settings-input-color) !important;}#help.popover{max-width:600px;--popover-arrow-offset:48px;}#help dt{float:left;clear:left;margin-right:0.5rem;}#help span.top,#help span.bottom{text-align:center;display:block;font-size:1.125rem;}#help span.top{margin:10px 0;border-bottom:1px solid var(--border-color);padding-bottom:4px;margin-bottom:6px;}#help span.bottom{clear:both;border-top:1px solid var(--border-color);}.side-by-side>div{width:50%;float:left;padding:0 20px 20px 17px;}.item-info .stab{min-height:36px;display:flex;padding:3px;margin-bottom:5px;align-items:center;vertical-align:text-bottom;}.item-name .stab{margin-left:0.3125em;}.stab{padding:0 2px;font-size:0.875rem;font-weight:normal;color:var(--main-color);background-color:var(--stab-background-color);width:fit-content;white-space:pre-wrap;border-radius:3px;display:inline;vertical-align:baseline;}.stab.portability>code{background:none;color:var(--stab-code-color);}.stab .emoji{font-size:1.25rem;margin-right:0.3rem;}.emoji{text-shadow:1px 0 0 black,-1px 0 0 black,0 1px 0 black,0 -1px 0 black;}.since{font-weight:normal;font-size:initial;}.rightside{padding-left:12px;float:right;}.rightside:not(a),.out-of-band{color:var(--right-side-color);}pre.rust{tab-size:4;-moz-tab-size:4;}pre.rust .kw{color:var(--code-highlight-kw-color);}pre.rust .kw-2{color:var(--code-highlight-kw-2-color);}pre.rust .lifetime{color:var(--code-highlight-lifetime-color);}pre.rust .prelude-ty{color:var(--code-highlight-prelude-color);}pre.rust .prelude-val{color:var(--code-highlight-prelude-val-color);}pre.rust .string{color:var(--code-highlight-string-color);}pre.rust .number{color:var(--code-highlight-number-color);}pre.rust .bool-val{color:var(--code-highlight-literal-color);}pre.rust .self{color:var(--code-highlight-self-color);}pre.rust .attr{color:var(--code-highlight-attribute-color);}pre.rust .macro,pre.rust .macro-nonterminal{color:var(--code-highlight-macro-color);}pre.rust .question-mark{font-weight:bold;color:var(--code-highlight-question-mark-color);}pre.rust .comment{color:var(--code-highlight-comment-color);}pre.rust .doccomment{color:var(--code-highlight-doc-comment-color);}.rustdoc.src .example-wrap pre.rust a{background:var(--codeblock-link-background);}.example-wrap.compile_fail,.example-wrap.should_panic{border-left:2px solid var(--codeblock-error-color);}.ignore.example-wrap{border-left:2px solid var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover,.example-wrap.should_panic:hover{border-left:2px solid var(--codeblock-error-hover-color);}.example-wrap.ignore:hover{border-left:2px solid var(--codeblock-ignore-hover-color);}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip{color:var(--codeblock-error-color);}.example-wrap.ignore .tooltip{color:var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover .tooltip,.example-wrap.should_panic:hover .tooltip{color:var(--codeblock-error-hover-color);}.example-wrap.ignore:hover .tooltip{color:var(--codeblock-ignore-hover-color);}.example-wrap .tooltip{position:absolute;display:block;left:-25px;top:5px;margin:0;line-height:1;}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip,.example-wrap.ignore .tooltip{font-weight:bold;font-size:1.25rem;}.content .docblock .warning{border-left:2px solid var(--warning-border-color);padding:14px;position:relative;overflow-x:visible !important;}.content .docblock .warning::before{color:var(--warning-border-color);content:"ⓘ";position:absolute;left:-25px;top:5px;font-weight:bold;font-size:1.25rem;}a.test-arrow{visibility:hidden;position:absolute;padding:5px 10px 5px 10px;border-radius:5px;font-size:1.375rem;top:5px;right:5px;z-index:1;color:var(--test-arrow-color);background-color:var(--test-arrow-background-color);}a.test-arrow:hover{color:var(--test-arrow-hover-color);background-color:var(--test-arrow-hover-background-color);}.example-wrap:hover .test-arrow{visibility:visible;}.code-attribute{font-weight:300;color:var(--code-attribute-color);}.item-spacer{width:100%;height:12px;display:block;}.out-of-band>span.since{font-size:1.25rem;}.sub-variant h4{font-size:1rem;font-weight:400;margin-top:0;margin-bottom:0;}.sub-variant{margin-left:24px;margin-bottom:40px;}.sub-variant>.sub-variant-field{margin-left:24px;}:target{padding-right:3px;background-color:var(--target-background-color);border-right:3px solid var(--target-border-color);}.code-header a.tooltip{color:inherit;margin-right:15px;position:relative;}.code-header a.tooltip:hover{color:var(--link-color);}a.tooltip:hover::after{position:absolute;top:calc(100% - 10px);left:-15px;right:-15px;height:20px;content:"\00a0";}.fade-out{opacity:0;transition:opacity 0.45s cubic-bezier(0,0,0.1,1.0);}.popover.tooltip .content{margin:0.25em 0.5em;}.popover.tooltip .content pre,.popover.tooltip .content code{background:transparent;margin:0;padding:0;font-size:1.25rem;white-space:pre-wrap;}.popover.tooltip .content>h3:first-child{margin:0 0 5px 0;}.search-failed{text-align:center;margin-top:20px;display:none;}.search-failed.active{display:block;}.search-failed>ul{text-align:left;max-width:570px;margin-left:auto;margin-right:auto;}#search-tabs{display:flex;flex-direction:row;gap:1px;margin-bottom:4px;}#search-tabs button{text-align:center;font-size:1.125rem;border:0;border-top:2px solid;flex:1;line-height:1.5;color:inherit;}#search-tabs button:not(.selected){background-color:var(--search-tab-button-not-selected-background);border-top-color:var(--search-tab-button-not-selected-border-top-color);}#search-tabs button:hover,#search-tabs button.selected{background-color:var(--search-tab-button-selected-background);border-top-color:var(--search-tab-button-selected-border-top-color);}#search-tabs .count{font-size:1rem;font-variant-numeric:tabular-nums;color:var(--search-tab-title-count-color);}#search .error code{border-radius:3px;background-color:var(--search-error-code-background-color);}.search-corrections{font-weight:normal;}#src-sidebar-toggle{position:sticky;top:0;left:0;font-size:1.25rem;border-bottom:1px solid;display:flex;height:40px;justify-content:stretch;align-items:stretch;z-index:10;}#src-sidebar{width:100%;overflow:auto;}#src-sidebar>.title{font-size:1.5rem;text-align:center;border-bottom:1px solid var(--border-color);margin-bottom:6px;}#src-sidebar div.files>a:hover,details.dir-entry summary:hover,#src-sidebar div.files>a:focus,details.dir-entry summary:focus{background-color:var(--src-sidebar-background-hover);}#src-sidebar div.files>a.selected{background-color:var(--src-sidebar-background-selected);}#src-sidebar-toggle>button{font-size:inherit;font-weight:bold;background:none;color:inherit;text-align:center;border:none;outline:none;flex:1 1;-webkit-appearance:none;opacity:1;}#settings-menu,#help-button{margin-left:4px;display:flex;}#settings-menu>a,#help-button>a{display:flex;align-items:center;justify-content:center;background-color:var(--button-background-color);border:1px solid var(--border-color);border-radius:2px;color:var(--settings-button-color);font-size:20px;width:33px;}#settings-menu>a:hover,#settings-menu>a:focus,#help-button>a:hover,#help-button>a:focus{border-color:var(--settings-button-border-focus);}#copy-path{color:var(--copy-path-button-color);background:var(--main-background-color);height:34px;margin-left:10px;padding:0;padding-left:2px;border:0;width:33px;}#copy-path>img{filter:var(--copy-path-img-filter);}#copy-path:hover>img{filter:var(--copy-path-img-hover-filter);}@keyframes rotating{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}#settings-menu.rotate>a img{animation:rotating 2s linear infinite;}kbd{display:inline-block;padding:3px 5px;font:15px monospace;line-height:10px;vertical-align:middle;border:solid 1px var(--border-color);border-radius:3px;color:var(--kbd-color);background-color:var(--kbd-background);box-shadow:inset 0 -1px 0 var(--kbd-box-shadow-color);}ul.all-items>li{list-style:none;}details.dir-entry{padding-left:4px;}details.dir-entry>summary{margin:0 0 0 -4px;padding:0 0 0 4px;cursor:pointer;}details.dir-entry div.folders,details.dir-entry div.files{padding-left:23px;}details.dir-entry a{display:block;}details.toggle{contain:layout;position:relative;}details.toggle>summary.hideme{cursor:pointer;font-size:1rem;}details.toggle>summary{list-style:none;outline:none;}details.toggle>summary::-webkit-details-marker,details.toggle>summary::marker{display:none;}details.toggle>summary.hideme>span{margin-left:9px;}details.toggle>summary::before{background:url('data:image/svg+xml,') no-repeat top left;content:"";cursor:pointer;width:16px;height:16px;display:inline-block;vertical-align:middle;opacity:.5;filter:var(--toggle-filter);}details.toggle>summary.hideme>span,.more-examples-toggle summary,.more-examples-toggle .hide-more{color:var(--toggles-color);}details.toggle>summary::after{content:"Expand";overflow:hidden;width:0;height:0;position:absolute;}details.toggle>summary.hideme::after{content:"";}details.toggle>summary:focus::before,details.toggle>summary:hover::before{opacity:1;}details.toggle>summary:focus-visible::before{outline:1px dotted #000;outline-offset:1px;}details.non-exhaustive{margin-bottom:8px;}details.toggle>summary.hideme::before{position:relative;}details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;top:4px;}.impl-items>details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;}details.toggle[open] >summary.hideme{position:absolute;}details.toggle[open] >summary.hideme>span{display:none;}details.toggle[open] >summary::before{background:url('data:image/svg+xml,') no-repeat top left;}details.toggle[open] >summary::after{content:"Collapse";}.docblock summary>*{display:inline-block;}.docblock>.example-wrap:first-child .tooltip{margin-top:16px;}@media (max-width:850px){#search-tabs .count{display:block;}}@media (max-width:700px){*[id]{scroll-margin-top:45px;}.rustdoc{display:block;}main{padding-left:15px;padding-top:0px;}.main-heading{flex-direction:column;}.out-of-band{text-align:left;margin-left:initial;padding:initial;}.out-of-band .since::before{content:"Since ";}.sidebar .logo-container,.sidebar .location{display:none;}.sidebar{position:fixed;top:45px;left:-1000px;z-index:11;height:calc(100vh - 45px);width:200px;}.src main,.rustdoc.src .sidebar{top:0;padding:0;height:100vh;border:0;}.sidebar.shown,.src-sidebar-expanded .src .sidebar,.rustdoc:not(.src) .sidebar:focus-within{left:0;}.mobile-topbar h2{padding-bottom:0;margin:auto 0.5em auto auto;overflow:hidden;font-size:24px;}.mobile-topbar h2 a{display:block;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;}.mobile-topbar .logo-container>img{max-width:35px;max-height:35px;margin:5px 0 5px 20px;}.mobile-topbar{display:flex;flex-direction:row;position:sticky;z-index:10;font-size:2rem;height:45px;width:100%;left:0;top:0;}.sidebar-menu-toggle{width:45px;font-size:32px;border:none;color:var(--main-color);}.sidebar-elems{margin-top:1em;}.anchor{display:none !important;}#main-content>details.toggle>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}#src-sidebar-toggle{position:fixed;left:1px;top:100px;width:30px;font-size:1.5rem;padding:0;z-index:10;border-top-right-radius:3px;border-bottom-right-radius:3px;border:1px solid;border-left:0;}.src-sidebar-expanded #src-sidebar-toggle{left:unset;top:unset;width:unset;border-top-right-radius:unset;border-bottom-right-radius:unset;position:sticky;border:0;border-bottom:1px solid;}#copy-path,#help-button{display:none;}.item-table,.item-row,.item-table>li,.item-table>li>div,.search-results>a,.search-results>a>div{display:block;}.search-results>a{padding:5px 0px;}.search-results>a>div.desc,.item-table>li>div.desc{padding-left:2em;}.search-results .result-name{display:block;}.search-results .result-name .typename{width:initial;margin-right:0;}.search-results .result-name .typename,.search-results .result-name .path{display:inline;}.src-sidebar-expanded .src .sidebar{max-width:100vw;width:100vw;}details.toggle:not(.top-doc)>summary{margin-left:10px;}.impl-items>details.toggle>summary:not(.hideme)::before,#main-content>details.toggle:not(.top-doc)>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}.impl-items>.item-info{margin-left:34px;}.src nav.sub{margin:0;padding:var(--nav-sub-mobile-padding);}}@media (min-width:701px){.scraped-example-title{position:absolute;z-index:10;background:var(--main-background-color);bottom:8px;right:5px;padding:2px 4px;box-shadow:0 0 4px var(--main-background-color);}}@media print{nav.sidebar,nav.sub,.out-of-band,a.src,#copy-path,details.toggle[open] >summary::before,details.toggle>summary::before,details.toggle.top-doc>summary{display:none;}.docblock{margin-left:0;}main{padding:10px;}}@media (max-width:464px){.docblock{margin-left:12px;}.docblock code{overflow-wrap:break-word;overflow-wrap:anywhere;}nav.sub{flex-direction:column;}.search-form{align-self:stretch;}.sub-logo-container>img{height:35px;width:35px;margin-bottom:var(--nav-sub-mobile-padding);}}.variant,.implementors-toggle>summary,.impl,#implementors-list>.docblock,.impl-items>section,.impl-items>.toggle>summary,.methods>section,.methods>.toggle>summary{margin-bottom:0.75em;}.variants>.docblock,.implementors-toggle>.docblock,.impl-items>.toggle[open]:not(:last-child),.methods>.toggle[open]:not(:last-child),.implementors-toggle[open]:not(:last-child){margin-bottom:2em;}#trait-implementations-list .impl-items>.toggle:not(:last-child),#synthetic-implementations-list .impl-items>.toggle:not(:last-child),#blanket-implementations-list .impl-items>.toggle:not(:last-child){margin-bottom:1em;}.scraped-example-list .scrape-help{margin-left:10px;padding:0 4px;font-weight:normal;font-size:12px;position:relative;bottom:1px;border:1px solid var(--scrape-example-help-border-color);border-radius:50px;color:var(--scrape-example-help-color);}.scraped-example-list .scrape-help:hover{border-color:var(--scrape-example-help-hover-border-color);color:var(--scrape-example-help-hover-color);}.scraped-example{position:relative;}.scraped-example .code-wrapper{position:relative;display:flex;flex-direction:row;flex-wrap:wrap;width:100%;}.scraped-example:not(.expanded) .code-wrapper{max-height:calc(1.5em * 5 + 10px);}.scraped-example:not(.expanded) .code-wrapper pre{overflow-y:hidden;padding-bottom:0;max-height:calc(1.5em * 5 + 10px);}.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper,.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper pre{max-height:calc(1.5em * 10 + 10px);}.scraped-example .code-wrapper .next,.scraped-example .code-wrapper .prev,.scraped-example .code-wrapper .expand{color:var(--main-color);position:absolute;top:0.25em;z-index:1;padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.scraped-example .code-wrapper .prev{right:2.25em;}.scraped-example .code-wrapper .next{right:1.25em;}.scraped-example .code-wrapper .expand{right:0.25em;}.scraped-example:not(.expanded) .code-wrapper::before,.scraped-example:not(.expanded) .code-wrapper::after{content:" ";width:100%;height:5px;position:absolute;z-index:1;}.scraped-example:not(.expanded) .code-wrapper::before{top:0;background:linear-gradient(to bottom,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example:not(.expanded) .code-wrapper::after{bottom:0;background:linear-gradient(to top,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example .code-wrapper .example-wrap{width:100%;overflow-y:hidden;margin-bottom:0;}.scraped-example:not(.expanded) .code-wrapper .example-wrap{overflow-x:hidden;}.scraped-example .example-wrap .rust span.highlight{background:var(--scrape-example-code-line-highlight);}.scraped-example .example-wrap .rust span.highlight.focus{background:var(--scrape-example-code-line-highlight-focus);}.more-examples-toggle{max-width:calc(100% + 25px);margin-top:10px;margin-left:-25px;}.more-examples-toggle .hide-more{margin-left:25px;cursor:pointer;}.more-scraped-examples{margin-left:25px;position:relative;}.toggle-line{position:absolute;top:5px;bottom:0;right:calc(100% + 10px);padding:0 4px;cursor:pointer;}.toggle-line-inner{min-width:2px;height:100%;background:var(--scrape-example-toggle-line-background);}.toggle-line:hover .toggle-line-inner{background:var(--scrape-example-toggle-line-hover-background);}.more-scraped-examples .scraped-example,.example-links{margin-top:20px;}.more-scraped-examples .scraped-example:first-child{margin-top:5px;}.example-links ul{margin-bottom:0;}:root[data-theme="light"]{--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--test-arrow-color:#f5f5f5;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#f5f5f5;--test-arrow-hover-background-color:rgb(78,139,202);--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);}:root[data-theme="dark"]{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--test-arrow-color:#dedede;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#dedede;--test-arrow-hover-background-color:#4e8bca;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);}:root[data-theme="ayu"]{--main-background-color:#0f1419;--main-color:#c5c5c5;--settings-input-color:#ffb454;--settings-input-border-color:#999;--settings-button-color:#fff;--settings-button-border-focus:#e0e0e0;--sidebar-background-color:#14191f;--sidebar-background-color-hover:rgba(70,70,70,0.33);--code-block-background-color:#191f26;--scrollbar-track-background-color:transparent;--scrollbar-thumb-background-color:#5c6773;--scrollbar-color:#5c6773 #24292f;--headings-border-bottom-color:#5c6773;--border-color:#5c6773;--button-background-color:#141920;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--search-input-focused-border-color:#5c6773;--copy-path-button-color:#fff;--copy-path-img-filter:invert(70%);--copy-path-img-hover-filter:invert(100%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ffa0a5;--trait-link-color:#39afd7;--assoc-item-link-color:#39afd7;--function-link-color:#fdd687;--macro-link-color:#a37acc;--keyword-link-color:#39afd7;--mod-link-color:#39afd7;--link-color:#39afd7;--sidebar-link-color:#53b1db;--sidebar-current-link-background-color:transparent;--search-result-link-focus-background-color:#3c3c3c;--search-result-border-color:#aaa3;--search-color:#fff;--search-error-code-background-color:#4f4c4c;--search-results-alias-color:#c5c5c5;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:none;--search-tab-button-not-selected-background:transparent !important;--search-tab-button-selected-border-top-color:none;--search-tab-button-selected-background:#141920 !important;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ff7733;--code-highlight-kw-2-color:#ff7733;--code-highlight-lifetime-color:#ff7733;--code-highlight-prelude-color:#69f2df;--code-highlight-prelude-val-color:#ff7733;--code-highlight-number-color:#b8cc52;--code-highlight-string-color:#b8cc52;--code-highlight-literal-color:#ff7733;--code-highlight-attribute-color:#e6e1cf;--code-highlight-self-color:#36a3d9;--code-highlight-macro-color:#a37acc;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#788797;--code-highlight-doc-comment-color:#a1ac88;--src-line-numbers-span-color:#5c6773;--src-line-number-highlighted-background-color:rgba(255,236,164,0.06);--test-arrow-color:#788797;--test-arrow-background-color:rgba(57,175,215,0.09);--test-arrow-hover-color:#c5c5c5;--test-arrow-hover-background-color:rgba(57,175,215,0.368);--target-background-color:rgba(255,236,164,0.06);--target-border-color:rgba(255,180,76,0.85);--kbd-color:#c5c5c5;--kbd-background:#314559;--kbd-box-shadow-color:#5c6773;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg) brightness(94%) contrast(94%);--crate-search-div-hover-filter:invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) brightness(113%) contrast(76%);--crate-search-hover-border:#e0e0e0;--src-sidebar-background-selected:#14191f;--src-sidebar-background-hover:#14191f;--table-alt-row-background-color:#191f26;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(15,20,25,1);--scrape-example-code-wrapper-background-end:rgba(15,20,25,0);}:root[data-theme="ayu"] h1,:root[data-theme="ayu"] h2,:root[data-theme="ayu"] h3,:root[data-theme="ayu"] h4,:where(:root[data-theme="ayu"]) h1 a,:root[data-theme="ayu"] .sidebar h2 a,:root[data-theme="ayu"] .sidebar h3 a,:root[data-theme="ayu"] #source-sidebar>.title{color:#fff;}:root[data-theme="ayu"] .docblock code{color:#ffb454;}:root[data-theme="ayu"] .docblock a>code{color:#39AFD7 !important;}:root[data-theme="ayu"] .code-header,:root[data-theme="ayu"] .docblock pre>code,:root[data-theme="ayu"] pre,:root[data-theme="ayu"] pre>code,:root[data-theme="ayu"] .item-info code,:root[data-theme="ayu"] .rustdoc.source .example-wrap{color:#e6e1cf;}:root[data-theme="ayu"] .sidebar .current,:root[data-theme="ayu"] .sidebar a:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:hover,:root[data-theme="ayu"] details.dir-entry summary:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:focus,:root[data-theme="ayu"] details.dir-entry summary:focus,:root[data-theme="ayu"] #src-sidebar div.files>a.selected{color:#ffb44c;}:root[data-theme="ayu"] .sidebar-elems .location{color:#ff7733;}:root[data-theme="ayu"] .src-line-numbers .line-highlighted{color:#708090;padding-right:7px;border-right:1px solid #ffb44c;}:root[data-theme="ayu"] .search-results a:hover,:root[data-theme="ayu"] .search-results a:focus{color:#fff !important;background-color:#3c3c3c;}:root[data-theme="ayu"] .search-results a{color:#0096cf;}:root[data-theme="ayu"] .search-results a div.desc{color:#c5c5c5;}:root[data-theme="ayu"] .result-name .primitive>i,:root[data-theme="ayu"] .result-name .keyword>i{color:#788797;}:root[data-theme="ayu"] #search-tabs>button.selected{border-bottom:1px solid #ffb44c !important;border-top:none;}:root[data-theme="ayu"] #search-tabs>button:not(.selected){border:none;background-color:transparent !important;}:root[data-theme="ayu"] #search-tabs>button:hover{border-bottom:1px solid rgba(242,151,24,0.3);}:root[data-theme="ayu"] #settings-menu>a img{filter:invert(100);} \ No newline at end of file diff --git a/static.files/scrape-examples-ef1e698c1d417c0c.js b/static.files/scrape-examples-ef1e698c1d417c0c.js new file mode 100644 index 0000000..ba830e3 --- /dev/null +++ b/static.files/scrape-examples-ef1e698c1d417c0c.js @@ -0,0 +1 @@ +"use strict";(function(){const DEFAULT_MAX_LINES=5;const HIDDEN_MAX_LINES=10;function scrollToLoc(elt,loc,isHidden){const lines=elt.querySelector(".src-line-numbers");let scrollOffset;const maxLines=isHidden?HIDDEN_MAX_LINES:DEFAULT_MAX_LINES;if(loc[1]-loc[0]>maxLines){const line=Math.max(0,loc[0]-1);scrollOffset=lines.children[line].offsetTop}else{const wrapper=elt.querySelector(".code-wrapper");const halfHeight=wrapper.offsetHeight/2;const offsetTop=lines.children[loc[0]].offsetTop;const lastLine=lines.children[loc[1]];const offsetBot=lastLine.offsetTop+lastLine.offsetHeight;const offsetMid=(offsetTop+offsetBot)/2;scrollOffset=offsetMid-halfHeight}lines.scrollTo(0,scrollOffset);elt.querySelector(".rust").scrollTo(0,scrollOffset)}function updateScrapedExample(example,isHidden){const locs=JSON.parse(example.attributes.getNamedItem("data-locs").textContent);let locIndex=0;const highlights=Array.prototype.slice.call(example.querySelectorAll(".highlight"));const link=example.querySelector(".scraped-example-title a");if(locs.length>1){const onChangeLoc=changeIndex=>{removeClass(highlights[locIndex],"focus");changeIndex();scrollToLoc(example,locs[locIndex][0],isHidden);addClass(highlights[locIndex],"focus");const url=locs[locIndex][1];const title=locs[locIndex][2];link.href=url;link.innerHTML=title};example.querySelector(".prev").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex-1+locs.length)%locs.length})});example.querySelector(".next").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex+1)%locs.length})})}const expandButton=example.querySelector(".expand");if(expandButton){expandButton.addEventListener("click",()=>{if(hasClass(example,"expanded")){removeClass(example,"expanded");scrollToLoc(example,locs[0][0],isHidden)}else{addClass(example,"expanded")}})}scrollToLoc(example,locs[0][0],isHidden)}const firstExamples=document.querySelectorAll(".scraped-example-list > .scraped-example");onEachLazy(firstExamples,el=>updateScrapedExample(el,false));onEachLazy(document.querySelectorAll(".more-examples-toggle"),toggle=>{onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"),button=>{button.addEventListener("click",()=>{toggle.open=false})});const moreExamples=toggle.querySelectorAll(".scraped-example");toggle.querySelector("summary").addEventListener("click",()=>{setTimeout(()=>{onEachLazy(moreExamples,el=>updateScrapedExample(el,true))})},{once:true})})})() \ No newline at end of file diff --git a/static.files/search-8fbf244ebcf71464.js b/static.files/search-8fbf244ebcf71464.js new file mode 100644 index 0000000..168023b --- /dev/null +++ b/static.files/search-8fbf244ebcf71464.js @@ -0,0 +1,5 @@ +"use strict";if(!Array.prototype.toSpliced){Array.prototype.toSpliced=function(){const me=this.slice();Array.prototype.splice.apply(me,arguments);return me}}(function(){const itemTypes=["mod","externcrate","import","struct","enum","fn","type","static","trait","impl","tymethod","method","structfield","variant","macro","primitive","associatedtype","constant","associatedconstant","union","foreigntype","keyword","existential","attr","derive","traitalias","generic",];const longItemTypes=["module","extern crate","re-export","struct","enum","function","type alias","static","trait","","trait method","method","struct field","enum variant","macro","primitive type","assoc type","constant","assoc const","union","foreign type","keyword","existential type","attribute macro","derive macro","trait alias",];const TY_PRIMITIVE=itemTypes.indexOf("primitive");const TY_KEYWORD=itemTypes.indexOf("keyword");const TY_GENERIC=itemTypes.indexOf("generic");const ROOT_PATH=typeof window!=="undefined"?window.rootPath:"../";function hasOwnPropertyRustdoc(obj,property){return Object.prototype.hasOwnProperty.call(obj,property)}function printTab(nb){let iter=0;let foundCurrentTab=false;let foundCurrentResultSet=false;onEachLazy(document.getElementById("search-tabs").childNodes,elem=>{if(nb===iter){addClass(elem,"selected");foundCurrentTab=true}else{removeClass(elem,"selected")}iter+=1});const isTypeSearch=(nb>0||iter===1);iter=0;onEachLazy(document.getElementById("results").childNodes,elem=>{if(nb===iter){addClass(elem,"active");foundCurrentResultSet=true}else{removeClass(elem,"active")}iter+=1});if(foundCurrentTab&&foundCurrentResultSet){searchState.currentTab=nb;const correctionsElem=document.getElementsByClassName("search-corrections");if(isTypeSearch){removeClass(correctionsElem[0],"hidden")}else{addClass(correctionsElem[0],"hidden")}}else if(nb!==0){printTab(0)}}const editDistanceState={current:[],prev:[],prevPrev:[],calculate:function calculate(a,b,limit){if(a.lengthlimit){return limit+1}while(b.length>0&&b[0]===a[0]){a=a.substring(1);b=b.substring(1)}while(b.length>0&&b[b.length-1]===a[a.length-1]){a=a.substring(0,a.length-1);b=b.substring(0,b.length-1)}if(b.length===0){return minDist}const aLength=a.length;const bLength=b.length;for(let i=0;i<=bLength;++i){this.current[i]=0;this.prev[i]=i;this.prevPrev[i]=Number.MAX_VALUE}for(let i=1;i<=aLength;++i){this.current[0]=i;const aIdx=i-1;for(let j=1;j<=bLength;++j){const bIdx=j-1;const substitutionCost=a[aIdx]===b[bIdx]?0:1;this.current[j]=Math.min(this.prev[j]+1,this.current[j-1]+1,this.prev[j-1]+substitutionCost);if((i>1)&&(j>1)&&(a[aIdx]===b[bIdx-1])&&(a[aIdx-1]===b[bIdx])){this.current[j]=Math.min(this.current[j],this.prevPrev[j-2]+1)}}const prevPrevTmp=this.prevPrev;this.prevPrev=this.prev;this.prev=this.current;this.current=prevPrevTmp}const distance=this.prev[bLength];return distance<=limit?distance:(limit+1)},};function editDistance(a,b,limit){return editDistanceState.calculate(a,b,limit)}function initSearch(rawSearchIndex){const MAX_RESULTS=200;const NO_TYPE_FILTER=-1;let searchIndex;let currentResults;let typeNameIdMap;const ALIASES=new Map();let typeNameIdOfArray;let typeNameIdOfSlice;let typeNameIdOfArrayOrSlice;function buildTypeMapIndex(name){if(name===""||name===null){return null}if(typeNameIdMap.has(name)){return typeNameIdMap.get(name)}else{const id=typeNameIdMap.size;typeNameIdMap.set(name,id);return id}}function isWhitespace(c){return" \t\n\r".indexOf(c)!==-1}function isSpecialStartCharacter(c){return"<\"".indexOf(c)!==-1}function isEndCharacter(c){return",>-]".indexOf(c)!==-1}function isStopCharacter(c){return isEndCharacter(c)}function isErrorCharacter(c){return"()".indexOf(c)!==-1}function itemTypeFromName(typename){const index=itemTypes.findIndex(i=>i===typename);if(index<0){throw["Unknown type filter ",typename]}return index}function getStringElem(query,parserState,isInGenerics){if(isInGenerics){throw["Unexpected ","\""," in generics"]}else if(query.literalSearch){throw["Cannot have more than one literal search element"]}else if(parserState.totalElems-parserState.genericsElems>0){throw["Cannot use literal search when there is more than one element"]}parserState.pos+=1;const start=parserState.pos;const end=getIdentEndPosition(parserState);if(parserState.pos>=parserState.length){throw["Unclosed ","\""]}else if(parserState.userQuery[end]!=="\""){throw["Unexpected ",parserState.userQuery[end]," in a string element"]}else if(start===end){throw["Cannot have empty string element"]}parserState.pos+=1;query.literalSearch=true}function isPathStart(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="::"}function isReturnArrow(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="->"}function isIdentCharacter(c){return(c==="_"||(c>="0"&&c<="9")||(c>="a"&&c<="z")||(c>="A"&&c<="Z"))}function isSeparatorCharacter(c){return c===","}function isPathSeparator(c){return c===":"||isWhitespace(c)}function prevIs(parserState,lookingFor){let pos=parserState.pos;while(pos>0){const c=parserState.userQuery[pos-1];if(c===lookingFor){return true}else if(!isWhitespace(c)){break}pos-=1}return false}function isLastElemGeneric(elems,parserState){return(elems.length>0&&elems[elems.length-1].generics.length>0)||prevIs(parserState,">")}function skipWhitespace(parserState){while(parserState.pos0){throw["Cannot have more than one element if you use quotes"]}const typeFilter=parserState.typeFilter;parserState.typeFilter=null;if(name==="!"){if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive never type ","!"," and ",typeFilter," both specified",]}if(generics.length!==0){throw["Never type ","!"," does not accept generic parameters",]}return{name:"never",id:null,fullPath:["never"],pathWithoutLast:[],pathLast:"never",generics:[],typeFilter:"primitive",}}if(path.startsWith("::")){throw["Paths cannot start with ","::"]}else if(path.endsWith("::")){throw["Paths cannot end with ","::"]}else if(path.includes("::::")){throw["Unexpected ","::::"]}else if(path.includes(" ::")){throw["Unexpected "," ::"]}else if(path.includes(":: ")){throw["Unexpected ",":: "]}const pathSegments=path.split(/::|\s+/);if(pathSegments.length===0||(pathSegments.length===1&&pathSegments[0]==="")){if(generics.length>0||prevIs(parserState,">")){throw["Found generics without a path"]}else{throw["Unexpected ",parserState.userQuery[parserState.pos]]}}for(const[i,pathSegment]of pathSegments.entries()){if(pathSegment==="!"){if(i!==0){throw["Never type ","!"," is not associated item"]}pathSegments[i]="never"}}parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1}return{name:name.trim(),id:null,fullPath:pathSegments,pathWithoutLast:pathSegments.slice(0,pathSegments.length-1),pathLast:pathSegments[pathSegments.length-1],generics:generics,typeFilter,}}function getIdentEndPosition(parserState){const start=parserState.pos;let end=parserState.pos;let foundExclamation=-1;while(parserState.pos=end){throw["Found generics without a path"]}parserState.pos+=1;getItemsBefore(query,parserState,generics,">")}if(isStringElem){skipWhitespace(parserState)}if(start>=end&&generics.length===0){return}elems.push(createQueryElement(query,parserState,parserState.userQuery.slice(start,end),generics,isInGenerics))}}function getItemsBefore(query,parserState,elems,endChar){let foundStopChar=true;let start=parserState.pos;const oldTypeFilter=parserState.typeFilter;parserState.typeFilter=null;let extra="";if(endChar===">"){extra="<"}else if(endChar==="]"){extra="["}else if(endChar===""){extra="->"}else{extra=endChar}while(parserState.pos"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(endChar!==""){throw["Expected ",","," or ",endChar,...extra,", found ",c,]}throw["Expected ",",",...extra,", found ",c,]}const posBefore=parserState.pos;start=parserState.pos;getNextElem(query,parserState,elems,endChar!=="");if(endChar!==""&&parserState.pos>=parserState.length){throw["Unclosed ",extra]}if(posBefore===parserState.pos){parserState.pos+=1}foundStopChar=false}if(parserState.pos>=parserState.length&&endChar!==""){throw["Unclosed ",extra]}parserState.pos+=1;parserState.typeFilter=oldTypeFilter}function checkExtraTypeFilterCharacters(start,parserState){const query=parserState.userQuery.slice(start,parserState.pos).trim();for(const c in query){if(!isIdentCharacter(query[c])){throw["Unexpected ",query[c]," in type filter (before ",":",")",]}}}function parseInput(query,parserState){let foundStopChar=true;let start=parserState.pos;while(parserState.pos"){if(isReturnArrow(parserState)){break}throw["Unexpected ",c," (did you mean ","->","?)"]}throw["Unexpected ",c]}else if(c===":"&&!isPathStart(parserState)){if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}else if(query.elems.length===0){throw["Expected type filter before ",":"]}else if(query.literalSearch){throw["Cannot use quotes on type filter"]}const typeFilterElem=query.elems.pop();checkExtraTypeFilterCharacters(start,parserState);parserState.typeFilter=typeFilterElem.name;parserState.pos+=1;parserState.totalElems-=1;query.literalSearch=false;foundStopChar=true;continue}else if(isWhitespace(c)){skipWhitespace(parserState);continue}if(!foundStopChar){let extra="";if(isLastElemGeneric(query.elems,parserState)){extra=[" after ",">"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(parserState.typeFilter!==null){throw["Expected ",","," or ","->",...extra,", found ",c,]}throw["Expected ",",",", ",":"," or ","->",...extra,", found ",c,]}const before=query.elems.length;start=parserState.pos;getNextElem(query,parserState,query.elems,false);if(query.elems.length===before){parserState.pos+=1}foundStopChar=false}if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}while(parserState.pos"]}break}else{parserState.pos+=1}}}function newParsedQuery(userQuery){return{original:userQuery,userQuery:userQuery.toLowerCase(),elems:[],returned:[],foundElems:0,totalElems:0,literalSearch:false,error:null,correction:null,proposeCorrectionFrom:null,proposeCorrectionTo:null,}}function buildUrl(search,filterCrates){let extra="?search="+encodeURIComponent(search);if(filterCrates!==null){extra+="&filter-crate="+encodeURIComponent(filterCrates)}return getNakedUrl()+extra+window.location.hash}function getFilterCrates(){const elem=document.getElementById("crate-search");if(elem&&elem.value!=="all crates"&&hasOwnPropertyRustdoc(rawSearchIndex,elem.value)){return elem.value}return null}function parseQuery(userQuery){function convertTypeFilterOnElem(elem){if(elem.typeFilter!==null){let typeFilter=elem.typeFilter;if(typeFilter==="const"){typeFilter="constant"}elem.typeFilter=itemTypeFromName(typeFilter)}else{elem.typeFilter=NO_TYPE_FILTER}for(const elem2 of elem.generics){convertTypeFilterOnElem(elem2)}}userQuery=userQuery.trim();const parserState={length:userQuery.length,pos:0,totalElems:0,genericsElems:0,typeFilter:null,userQuery:userQuery.toLowerCase(),};let query=newParsedQuery(userQuery);try{parseInput(query,parserState);for(const elem of query.elems){convertTypeFilterOnElem(elem)}for(const elem of query.returned){convertTypeFilterOnElem(elem)}}catch(err){query=newParsedQuery(userQuery);query.error=err;return query}if(!query.literalSearch){query.literalSearch=parserState.totalElems>1}query.foundElems=query.elems.length+query.returned.length;query.totalElems=parserState.totalElems;return query}function createQueryResults(results_in_args,results_returned,results_others,parsedQuery){return{"in_args":results_in_args,"returned":results_returned,"others":results_others,"query":parsedQuery,}}function execQuery(parsedQuery,searchWords,filterCrates,currentCrate){const results_others=new Map(),results_in_args=new Map(),results_returned=new Map();function transformResults(results){const duplicates=new Set();const out=[];for(const result of results){if(result.id!==-1){const obj=searchIndex[result.id];obj.dist=result.dist;const res=buildHrefAndPath(obj);obj.displayPath=pathSplitter(res[0]);obj.fullPath=obj.displayPath+obj.name;obj.fullPath+="|"+obj.ty;if(duplicates.has(obj.fullPath)){continue}duplicates.add(obj.fullPath);obj.href=res[1];out.push(obj);if(out.length>=MAX_RESULTS){break}}}return out}function sortResults(results,isType,preferredCrate){if(results.size===0){return[]}const userQuery=parsedQuery.userQuery;const result_list=[];for(const result of results.values()){result.word=searchWords[result.id];result.item=searchIndex[result.id]||{};result_list.push(result)}result_list.sort((aaa,bbb)=>{let a,b;a=(aaa.word!==userQuery);b=(bbb.word!==userQuery);if(a!==b){return a-b}a=(aaa.index<0);b=(bbb.index<0);if(a!==b){return a-b}a=aaa.path_dist;b=bbb.path_dist;if(a!==b){return a-b}a=aaa.index;b=bbb.index;if(a!==b){return a-b}a=(aaa.dist);b=(bbb.dist);if(a!==b){return a-b}a=aaa.item.deprecated;b=bbb.item.deprecated;if(a!==b){return a-b}a=(aaa.item.crate!==preferredCrate);b=(bbb.item.crate!==preferredCrate);if(a!==b){return a-b}a=aaa.word.length;b=bbb.word.length;if(a!==b){return a-b}a=aaa.word;b=bbb.word;if(a!==b){return(a>b?+1:-1)}if((aaa.item.ty===TY_PRIMITIVE&&bbb.item.ty!==TY_KEYWORD)||(aaa.item.ty===TY_KEYWORD&&bbb.item.ty!==TY_PRIMITIVE)){return-1}if((bbb.item.ty===TY_PRIMITIVE&&aaa.item.ty!==TY_PRIMITIVE)||(bbb.item.ty===TY_KEYWORD&&aaa.item.ty!==TY_KEYWORD)){return 1}a=(aaa.item.desc==="");b=(bbb.item.desc==="");if(a!==b){return a-b}a=aaa.item.ty;b=bbb.item.ty;if(a!==b){return a-b}a=aaa.item.path;b=bbb.item.path;if(a!==b){return(a>b?+1:-1)}return 0});let nameSplit=null;if(parsedQuery.elems.length===1){const hasPath=typeof parsedQuery.elems[0].path==="undefined";nameSplit=hasPath?null:parsedQuery.elems[0].path}for(const result of result_list){if(result.dontValidate){continue}const name=result.item.name.toLowerCase(),path=result.item.path.toLowerCase(),parent=result.item.parent;if(!isType&&!validateResult(name,path,nameSplit,parent)){result.id=-1}}return transformResults(result_list)}function checkGenerics(fnType,queryElem,whereClause,mgensInout){return unifyFunctionTypes(fnType.generics,queryElem.generics,whereClause,mgensInout,mgens=>{if(mgensInout){for(const[fid,qid]of mgens.entries()){mgensInout.set(fid,qid)}}return true})}function unifyFunctionTypes(fnTypesIn,queryElems,whereClause,mgensIn,solutionCb){let mgens=new Map(mgensIn);if(queryElems.length===0){return!solutionCb||solutionCb(mgens)}if(!fnTypesIn||fnTypesIn.length===0){return false}const ql=queryElems.length;let fl=fnTypesIn.length;let fnTypes=fnTypesIn.slice();const backtracking=[];let i=0;let j=0;const backtrack=()=>{while(backtracking.length!==0){const{fnTypesScratch,mgensScratch,queryElemsOffset,fnTypesOffset,unbox,}=backtracking.pop();mgens=new Map(mgensScratch);const fnType=fnTypesScratch[fnTypesOffset];const queryElem=queryElems[queryElemsOffset];if(unbox){if(fnType.id<0){if(mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){continue}mgens.set(fnType.id,0)}const generics=fnType.id<0?whereClause[(-fnType.id)-1]:fnType.generics;fnTypes=fnTypesScratch.toSpliced(fnTypesOffset,1,...generics);fl=fnTypes.length;i=queryElemsOffset-1}else{if(fnType.id<0){if(mgens.has(fnType.id)&&mgens.get(fnType.id)!==queryElem.id){continue}mgens.set(fnType.id,queryElem.id)}fnTypes=fnTypesScratch.slice();fl=fnTypes.length;const tmp=fnTypes[queryElemsOffset];fnTypes[queryElemsOffset]=fnTypes[fnTypesOffset];fnTypes[fnTypesOffset]=tmp;i=queryElemsOffset}return true}return false};for(i=0;i!==ql;++i){const queryElem=queryElems[i];const matchCandidates=[];let fnTypesScratch=null;let mgensScratch=null;for(j=i;j!==fl;++j){const fnType=fnTypes[j];if(unifyFunctionTypeIsMatchCandidate(fnType,queryElem,whereClause,mgens)){if(!fnTypesScratch){fnTypesScratch=fnTypes.slice()}unifyFunctionTypes(fnType.generics,queryElem.generics,whereClause,mgens,mgensScratch=>{matchCandidates.push({fnTypesScratch,mgensScratch,queryElemsOffset:i,fnTypesOffset:j,unbox:false,});return false})}if(unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens)){if(!fnTypesScratch){fnTypesScratch=fnTypes.slice()}if(!mgensScratch){mgensScratch=new Map(mgens)}backtracking.push({fnTypesScratch,mgensScratch,queryElemsOffset:i,fnTypesOffset:j,unbox:true,})}}if(matchCandidates.length===0){if(backtrack()){continue}else{return false}}const{fnTypesOffset:candidate,mgensScratch:mgensNew}=matchCandidates.pop();if(fnTypes[candidate].id<0&&queryElems[i].id<0){mgens.set(fnTypes[candidate].id,queryElems[i].id)}for(const[fid,qid]of mgensNew){mgens.set(fid,qid)}const tmp=fnTypes[candidate];fnTypes[candidate]=fnTypes[i];fnTypes[i]=tmp;for(const otherCandidate of matchCandidates){backtracking.push(otherCandidate)}while(i===(ql-1)&&solutionCb&&!solutionCb(mgens)){if(!backtrack()){return false}}}return true}function unifyFunctionTypeIsMatchCandidate(fnType,queryElem,whereClause,mgens){if(!typePassesFilter(queryElem.typeFilter,fnType.ty)){return false}if(fnType.id<0&&queryElem.id<0){if(mgens.has(fnType.id)&&mgens.get(fnType.id)!==queryElem.id){return false}for(const[fid,qid]of mgens.entries()){if(fnType.id!==fid&&queryElem.id===qid){return false}if(fnType.id===fid&&queryElem.id!==qid){return false}}}else{if(queryElem.id===typeNameIdOfArrayOrSlice&&(fnType.id===typeNameIdOfSlice||fnType.id===typeNameIdOfArray)){}else if(fnType.id!==queryElem.id){return false}if(fnType.generics.length===0&&queryElem.generics.length!==0){return false}const queryElemPathLength=queryElem.pathWithoutLast.length;if(queryElemPathLength>0){const fnTypePath=fnType.path!==undefined&&fnType.path!==null?fnType.path.split("::"):[];if(queryElemPathLength>fnTypePath.length){return false}let i=0;for(const path of fnTypePath){if(path===queryElem.pathWithoutLast[i]){i+=1;if(i>=queryElemPathLength){break}}}if(i=0){if(!whereClause){return false}if(mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){return false}return checkIfInList(whereClause[(-fnType.id)-1],queryElem,whereClause)}else if(fnType.generics&&fnType.generics.length>0){return checkIfInList(fnType.generics,queryElem,whereClause)}return false}function checkIfInList(list,elem,whereClause){for(const entry of list){if(checkType(entry,elem,whereClause)){return true}}return false}function checkType(row,elem,whereClause){if(row.id===null){return row.generics.length>0?checkIfInList(row.generics,elem,whereClause):false}if(row.id<0&&elem.id>=0){const gid=(-row.id)-1;return checkIfInList(whereClause[gid],elem,whereClause)}if(row.id<0&&elem.id<0){return true}const matchesExact=row.id===elem.id;const matchesArrayOrSlice=elem.id===typeNameIdOfArrayOrSlice&&(row.id===typeNameIdOfSlice||row.id===typeNameIdOfArray);if((matchesExact||matchesArrayOrSlice)&&typePassesFilter(elem.typeFilter,row.ty)){if(elem.generics.length>0){return checkGenerics(row,elem,whereClause,new Map())}return true}return checkIfInList(row.generics,elem,whereClause)}function checkPath(contains,ty,maxEditDistance){if(contains.length===0){return 0}let ret_dist=maxEditDistance+1;const path=ty.path.split("::");if(ty.parent&&ty.parent.name){path.push(ty.parent.name.toLowerCase())}const length=path.length;const clength=contains.length;if(clength>length){return maxEditDistance+1}for(let i=0;ilength){break}let dist_total=0;let aborted=false;for(let x=0;xmaxEditDistance){aborted=true;break}dist_total+=dist}if(!aborted){ret_dist=Math.min(ret_dist,Math.round(dist_total/clength))}}return ret_dist}function typePassesFilter(filter,type){if(filter<=NO_TYPE_FILTER||filter===type)return true;const name=itemTypes[type];switch(itemTypes[filter]){case"constant":return name==="associatedconstant";case"fn":return name==="method"||name==="tymethod";case"type":return name==="primitive"||name==="associatedtype";case"trait":return name==="traitalias"}return false}function createAliasFromItem(item){return{crate:item.crate,name:item.name,path:item.path,desc:item.desc,ty:item.ty,parent:item.parent,type:item.type,is_alias:true,deprecated:item.deprecated,implDisambiguator:item.implDisambiguator,}}function handleAliases(ret,query,filterCrates,currentCrate){const lowerQuery=query.toLowerCase();const aliases=[];const crateAliases=[];if(filterCrates!==null){if(ALIASES.has(filterCrates)&&ALIASES.get(filterCrates).has(lowerQuery)){const query_aliases=ALIASES.get(filterCrates).get(lowerQuery);for(const alias of query_aliases){aliases.push(createAliasFromItem(searchIndex[alias]))}}}else{for(const[crate,crateAliasesIndex]of ALIASES){if(crateAliasesIndex.has(lowerQuery)){const pushTo=crate===currentCrate?crateAliases:aliases;const query_aliases=crateAliasesIndex.get(lowerQuery);for(const alias of query_aliases){pushTo.push(createAliasFromItem(searchIndex[alias]))}}}}const sortFunc=(aaa,bbb)=>{if(aaa.path{alias.alias=query;const res=buildHrefAndPath(alias);alias.displayPath=pathSplitter(res[0]);alias.fullPath=alias.displayPath+alias.name;alias.href=res[1];ret.others.unshift(alias);if(ret.others.length>MAX_RESULTS){ret.others.pop()}};aliases.forEach(pushFunc);crateAliases.forEach(pushFunc)}function addIntoResults(results,fullId,id,index,dist,path_dist,maxEditDistance){const inBounds=dist<=maxEditDistance||index!==-1;if(dist===0||(!parsedQuery.literalSearch&&inBounds)){if(results.has(fullId)){const result=results.get(fullId);if(result.dontValidate||result.dist<=dist){return}}results.set(fullId,{id:id,index:index,dontValidate:parsedQuery.literalSearch,dist:dist,path_dist:path_dist,})}}function handleSingleArg(row,pos,elem,results_others,results_in_args,results_returned,maxEditDistance){if(!row||(filterCrates!==null&&row.crate!==filterCrates)){return}let index=-1,path_dist=0;const fullId=row.id;const searchWord=searchWords[pos];const in_args=row.type&&row.type.inputs&&checkIfInList(row.type.inputs,elem,row.type.where_clause);if(in_args){addIntoResults(results_in_args,fullId,pos,-1,0,0,maxEditDistance)}const returned=row.type&&row.type.output&&checkIfInList(row.type.output,elem,row.type.where_clause);if(returned){addIntoResults(results_returned,fullId,pos,-1,0,0,maxEditDistance)}if(!typePassesFilter(elem.typeFilter,row.ty)){return}const row_index=row.normalizedName.indexOf(elem.pathLast);const word_index=searchWord.indexOf(elem.pathLast);if(row_index===-1){index=word_index}else if(word_index===-1){index=row_index}else if(word_index1){path_dist=checkPath(elem.pathWithoutLast,row,maxEditDistance);if(path_dist>maxEditDistance){return}}if(parsedQuery.literalSearch){if(searchWord===elem.name){addIntoResults(results_others,fullId,pos,index,0,path_dist)}return}const dist=editDistance(searchWord,elem.pathLast,maxEditDistance);if(index===-1&&dist+path_dist>maxEditDistance){return}addIntoResults(results_others,fullId,pos,index,dist,path_dist,maxEditDistance)}function handleArgs(row,pos,results){if(!row||(filterCrates!==null&&row.crate!==filterCrates)||!row.type){return}if(!unifyFunctionTypes(row.type.inputs,parsedQuery.elems,row.type.where_clause,null,mgens=>{return unifyFunctionTypes(row.type.output,parsedQuery.returned,row.type.where_clause,mgens)})){return}addIntoResults(results,row.id,pos,0,0,0,Number.MAX_VALUE)}function innerRunQuery(){let elem,i,nSearchWords,in_returned,row;let queryLen=0;for(const elem of parsedQuery.elems){queryLen+=elem.name.length}for(const elem of parsedQuery.returned){queryLen+=elem.name.length}const maxEditDistance=Math.floor(queryLen/3);const genericSymbols=new Map();function convertNameToId(elem){if(typeNameIdMap.has(elem.pathLast)){elem.id=typeNameIdMap.get(elem.pathLast)}else if(!parsedQuery.literalSearch){let match=null;let matchDist=maxEditDistance+1;let matchName="";for(const[name,id]of typeNameIdMap){const dist=editDistance(name,elem.pathLast,maxEditDistance);if(dist<=matchDist&&dist<=maxEditDistance){if(dist===matchDist&&matchName>name){continue}match=id;matchDist=dist;matchName=name}}if(match!==null){parsedQuery.correction=matchName}elem.id=match}if((elem.id===null&&parsedQuery.totalElems>1&&elem.typeFilter===-1&&elem.generics.length===0)||elem.typeFilter===TY_GENERIC){if(genericSymbols.has(elem.name)){elem.id=genericSymbols.get(elem.name)}else{elem.id=-(genericSymbols.size+1);genericSymbols.set(elem.name,elem.id)}if(elem.typeFilter===-1&&elem.name.length>=3){const maxPartDistance=Math.floor(elem.name.length/3);let matchDist=maxPartDistance+1;let matchName="";for(const name of typeNameIdMap.keys()){const dist=editDistance(name,elem.name,maxPartDistance);if(dist<=matchDist&&dist<=maxPartDistance){if(dist===matchDist&&matchName>name){continue}matchDist=dist;matchName=name}}if(matchName!==""){parsedQuery.proposeCorrectionFrom=elem.name;parsedQuery.proposeCorrectionTo=matchName}}elem.typeFilter=TY_GENERIC}if(elem.generics.length>0&&elem.typeFilter===TY_GENERIC){parsedQuery.error=["Generic type parameter ",elem.name," does not accept generic parameters",]}for(const elem2 of elem.generics){convertNameToId(elem2)}}for(const elem of parsedQuery.elems){convertNameToId(elem)}for(const elem of parsedQuery.returned){convertNameToId(elem)}if(parsedQuery.foundElems===1){if(parsedQuery.elems.length===1){elem=parsedQuery.elems[0];for(i=0,nSearchWords=searchWords.length;i0){for(i=0,nSearchWords=searchWords.length;i-1||path.indexOf(key)>-1||(parent!==undefined&&parent.name!==undefined&&parent.name.toLowerCase().indexOf(key)>-1)||editDistance(name,key,maxEditDistance)<=maxEditDistance)){return false}}return true}function nextTab(direction){const next=(searchState.currentTab+direction+3)%searchState.focusedByTab.length;searchState.focusedByTab[searchState.currentTab]=document.activeElement;printTab(next);focusSearchResult()}function focusSearchResult(){const target=searchState.focusedByTab[searchState.currentTab]||document.querySelectorAll(".search-results.active a").item(0)||document.querySelectorAll("#search-tabs button").item(searchState.currentTab);searchState.focusedByTab[searchState.currentTab]=null;if(target){target.focus()}}function buildHrefAndPath(item){let displayPath;let href;const type=itemTypes[item.ty];const name=item.name;let path=item.path;if(type==="mod"){displayPath=path+"::";href=ROOT_PATH+path.replace(/::/g,"/")+"/"+name+"/index.html"}else if(type==="import"){displayPath=item.path+"::";href=ROOT_PATH+item.path.replace(/::/g,"/")+"/index.html#reexport."+name}else if(type==="primitive"||type==="keyword"){displayPath="";href=ROOT_PATH+path.replace(/::/g,"/")+"/"+type+"."+name+".html"}else if(type==="externcrate"){displayPath="";href=ROOT_PATH+name+"/index.html"}else if(item.parent!==undefined){const myparent=item.parent;let anchor=type+"."+name;const parentType=itemTypes[myparent.ty];let pageType=parentType;let pageName=myparent.name;if(parentType==="primitive"){displayPath=myparent.name+"::"}else if(type==="structfield"&&parentType==="variant"){const enumNameIdx=item.path.lastIndexOf("::");const enumName=item.path.substr(enumNameIdx+2);path=item.path.substr(0,enumNameIdx);displayPath=path+"::"+enumName+"::"+myparent.name+"::";anchor="variant."+myparent.name+".field."+name;pageType="enum";pageName=enumName}else{displayPath=path+"::"+myparent.name+"::"}if(item.implDisambiguator!==null){anchor=item.implDisambiguator+"/"+anchor}href=ROOT_PATH+path.replace(/::/g,"/")+"/"+pageType+"."+pageName+".html#"+anchor}else{displayPath=item.path+"::";href=ROOT_PATH+item.path.replace(/::/g,"/")+"/"+type+"."+name+".html"}return[displayPath,href]}function pathSplitter(path){const tmp=""+path.replace(/::/g,"::");if(tmp.endsWith("")){return tmp.slice(0,tmp.length-6)}return tmp}function addTab(array,query,display){let extraClass="";if(display===true){extraClass=" active"}const output=document.createElement("div");let length=0;if(array.length>0){output.className="search-results "+extraClass;array.forEach(item=>{const name=item.name;const type=itemTypes[item.ty];const longType=longItemTypes[item.ty];const typeName=longType.length!==0?`${longType}`:"?";length+=1;const link=document.createElement("a");link.className="result-"+type;link.href=item.href;const resultName=document.createElement("div");resultName.className="result-name";resultName.insertAdjacentHTML("beforeend",`${typeName}`);link.appendChild(resultName);let alias=" ";if(item.is_alias){alias=`
\ +${item.alias} - see \ +
`}resultName.insertAdjacentHTML("beforeend",`
${alias}\ +${item.displayPath}${name}\ +
`);const description=document.createElement("div");description.className="desc";description.insertAdjacentHTML("beforeend",item.desc);link.appendChild(description);output.appendChild(link)})}else if(query.error===null){output.className="search-failed"+extraClass;output.innerHTML="No results :(
"+"Try on DuckDuckGo?

"+"Or try looking in one of these:"}return[output,length]}function makeTabHeader(tabNb,text,nbElems){const fmtNbElems=nbElems<10?`\u{2007}(${nbElems})\u{2007}\u{2007}`:nbElems<100?`\u{2007}(${nbElems})\u{2007}`:`\u{2007}(${nbElems})`;if(searchState.currentTab===tabNb){return""}return""}function showResults(results,go_to_first,filterCrates){const search=searchState.outputElement();if(go_to_first||(results.others.length===1&&getSettingValue("go-to-only-result")==="true")){window.onunload=()=>{};searchState.removeQueryParameters();const elem=document.createElement("a");elem.href=results.others[0].href;removeClass(elem,"active");document.body.appendChild(elem);elem.click();return}if(results.query===undefined){results.query=parseQuery(searchState.input.value)}currentResults=results.query.userQuery;const ret_others=addTab(results.others,results.query,true);const ret_in_args=addTab(results.in_args,results.query,false);const ret_returned=addTab(results.returned,results.query,false);let currentTab=searchState.currentTab;if((currentTab===0&&ret_others[1]===0)||(currentTab===1&&ret_in_args[1]===0)||(currentTab===2&&ret_returned[1]===0)){if(ret_others[1]!==0){currentTab=0}else if(ret_in_args[1]!==0){currentTab=1}else if(ret_returned[1]!==0){currentTab=2}}let crates="";const crates_list=Object.keys(rawSearchIndex);if(crates_list.length>1){crates=" in 
"}let output=`

Results${crates}

`;if(results.query.error!==null){const error=results.query.error;error.forEach((value,index)=>{value=value.split("<").join("<").split(">").join(">");if(index%2!==0){error[index]=`${value.replaceAll(" ", " ")}`}else{error[index]=value}});output+=`

Query parser error: "${error.join("")}".

`;output+="
"+makeTabHeader(0,"In Names",ret_others[1])+"
";currentTab=0}else if(results.query.foundElems<=1&&results.query.returned.length===0){output+="
"+makeTabHeader(0,"In Names",ret_others[1])+makeTabHeader(1,"In Parameters",ret_in_args[1])+makeTabHeader(2,"In Return Types",ret_returned[1])+"
"}else{const signatureTabTitle=results.query.elems.length===0?"In Function Return Types":results.query.returned.length===0?"In Function Parameters":"In Function Signatures";output+="
"+makeTabHeader(0,signatureTabTitle,ret_others[1])+"
";currentTab=0}if(results.query.correction!==null){const orig=results.query.returned.length>0?results.query.returned[0].name:results.query.elems[0].name;output+="

"+`Type "${orig}" not found. `+"Showing results for closest type name "+`"${results.query.correction}" instead.

`}if(results.query.proposeCorrectionFrom!==null){const orig=results.query.proposeCorrectionFrom;const targ=results.query.proposeCorrectionTo;output+="

"+`Type "${orig}" not found and used as generic parameter. `+`Consider searching for "${targ}" instead.

`}const resultsElem=document.createElement("div");resultsElem.id="results";resultsElem.appendChild(ret_others[0]);resultsElem.appendChild(ret_in_args[0]);resultsElem.appendChild(ret_returned[0]);search.innerHTML=output;const crateSearch=document.getElementById("crate-search");if(crateSearch){crateSearch.addEventListener("input",updateCrate)}search.appendChild(resultsElem);searchState.showResults(search);const elems=document.getElementById("search-tabs").childNodes;searchState.focusedByTab=[];let i=0;for(const elem of elems){const j=i;elem.onclick=()=>printTab(j);searchState.focusedByTab.push(null);i+=1}printTab(currentTab)}function updateSearchHistory(url){if(!browserSupportsHistoryApi()){return}const params=searchState.getQueryStringParams();if(!history.state&&!params.search){history.pushState(null,"",url)}else{history.replaceState(null,"",url)}}function search(e,forced){if(e){e.preventDefault()}const query=parseQuery(searchState.input.value.trim());let filterCrates=getFilterCrates();if(!forced&&query.userQuery===currentResults){if(query.userQuery.length>0){putBackSearch()}return}searchState.setLoadingSearch();const params=searchState.getQueryStringParams();if(filterCrates===null&¶ms["filter-crate"]!==undefined){filterCrates=params["filter-crate"]}searchState.title="Results for "+query.original+" - Rust";updateSearchHistory(buildUrl(query.original,filterCrates));showResults(execQuery(query,searchWords,filterCrates,window.currentCrate),params.go_to_first,filterCrates)}function buildItemSearchTypeAll(types,lowercasePaths){return types.map(type=>buildItemSearchType(type,lowercasePaths))}function buildItemSearchType(type,lowercasePaths){const PATH_INDEX_DATA=0;const GENERICS_DATA=1;let pathIndex,generics;if(typeof type==="number"){pathIndex=type;generics=[]}else{pathIndex=type[PATH_INDEX_DATA];generics=buildItemSearchTypeAll(type[GENERICS_DATA],lowercasePaths)}if(pathIndex<0){return{id:pathIndex,ty:TY_GENERIC,path:null,generics,}}if(pathIndex===0){return{id:null,ty:null,path:null,generics,}}const item=lowercasePaths[pathIndex-1];return{id:buildTypeMapIndex(item.name),ty:item.ty,path:item.path,generics,}}function buildFunctionSearchType(functionSearchType,lowercasePaths){const INPUTS_DATA=0;const OUTPUT_DATA=1;if(functionSearchType===0){return null}let inputs,output;if(typeof functionSearchType[INPUTS_DATA]==="number"){inputs=[buildItemSearchType(functionSearchType[INPUTS_DATA],lowercasePaths)]}else{inputs=buildItemSearchTypeAll(functionSearchType[INPUTS_DATA],lowercasePaths)}if(functionSearchType.length>1){if(typeof functionSearchType[OUTPUT_DATA]==="number"){output=[buildItemSearchType(functionSearchType[OUTPUT_DATA],lowercasePaths)]}else{output=buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA],lowercasePaths)}}else{output=[]}const where_clause=[];const l=functionSearchType.length;for(let i=2;i2){path=itemPaths.has(elem[2])?itemPaths.get(elem[2]):lastPath;lastPath=path}lowercasePaths.push({ty:ty,name:name.toLowerCase(),path:path});paths[i]={ty:ty,name:name,path:path}}lastPath="";len=itemTypes.length;for(let i=0;i0?paths[itemParentIdxs[i]-1]:undefined,type:buildFunctionSearchType(itemFunctionSearchTypes[i],lowercasePaths),id:id,normalizedName:word.indexOf("_")===-1?word:word.replace(/_/g,""),deprecated:deprecatedItems.has(i),implDisambiguator:implDisambiguator.has(i)?implDisambiguator.get(i):null,};id+=1;searchIndex.push(row);lastPath=row.path;crateSize+=1}if(aliases){const currentCrateAliases=new Map();ALIASES.set(crate,currentCrateAliases);for(const alias_name in aliases){if(!hasOwnPropertyRustdoc(aliases,alias_name)){continue}let currentNameAliases;if(currentCrateAliases.has(alias_name)){currentNameAliases=currentCrateAliases.get(alias_name)}else{currentNameAliases=[];currentCrateAliases.set(alias_name,currentNameAliases)}for(const local_alias of aliases[alias_name]){currentNameAliases.push(local_alias+currentIndex)}}}currentIndex+=crateSize}return searchWords}function onSearchSubmit(e){e.preventDefault();searchState.clearInputTimeout();search()}function putBackSearch(){const search_input=searchState.input;if(!searchState.input){return}if(search_input.value!==""&&!searchState.isDisplayed()){searchState.showResults();if(browserSupportsHistoryApi()){history.replaceState(null,"",buildUrl(search_input.value,getFilterCrates()))}document.title=searchState.title}}function registerSearchEvents(){const params=searchState.getQueryStringParams();if(searchState.input.value===""){searchState.input.value=params.search||""}const searchAfter500ms=()=>{searchState.clearInputTimeout();if(searchState.input.value.length===0){searchState.hideResults()}else{searchState.timeout=setTimeout(search,500)}};searchState.input.onkeyup=searchAfter500ms;searchState.input.oninput=searchAfter500ms;document.getElementsByClassName("search-form")[0].onsubmit=onSearchSubmit;searchState.input.onchange=e=>{if(e.target!==document.activeElement){return}searchState.clearInputTimeout();setTimeout(search,0)};searchState.input.onpaste=searchState.input.onchange;searchState.outputElement().addEventListener("keydown",e=>{if(e.altKey||e.ctrlKey||e.shiftKey||e.metaKey){return}if(e.which===38){const previous=document.activeElement.previousElementSibling;if(previous){previous.focus()}else{searchState.focus()}e.preventDefault()}else if(e.which===40){const next=document.activeElement.nextElementSibling;if(next){next.focus()}const rect=document.activeElement.getBoundingClientRect();if(window.innerHeight-rect.bottom{if(e.which===40){focusSearchResult();e.preventDefault()}});searchState.input.addEventListener("focus",()=>{putBackSearch()});searchState.input.addEventListener("blur",()=>{searchState.input.placeholder=searchState.input.origPlaceholder});if(browserSupportsHistoryApi()){const previousTitle=document.title;window.addEventListener("popstate",e=>{const params=searchState.getQueryStringParams();document.title=previousTitle;currentResults=null;if(params.search&¶ms.search.length>0){searchState.input.value=params.search;search(e)}else{searchState.input.value="";searchState.hideResults()}})}window.onpageshow=()=>{const qSearch=searchState.getQueryStringParams().search;if(searchState.input.value===""&&qSearch){searchState.input.value=qSearch}search()}}function updateCrate(ev){if(ev.target.value==="all crates"){const query=searchState.input.value.trim();updateSearchHistory(buildUrl(query,null))}currentResults=null;search(undefined,true)}const searchWords=buildIndex(rawSearchIndex);if(typeof window!=="undefined"){registerSearchEvents();if(window.searchState.getQueryStringParams().search){search()}}if(typeof exports!=="undefined"){exports.initSearch=initSearch;exports.execQuery=execQuery;exports.parseQuery=parseQuery}return searchWords}if(typeof window!=="undefined"){window.initSearch=initSearch;if(window.searchIndex!==undefined){initSearch(window.searchIndex)}}else{initSearch({})}})() \ No newline at end of file diff --git a/static.files/settings-74424d7eec62a23e.js b/static.files/settings-74424d7eec62a23e.js new file mode 100644 index 0000000..3014f75 --- /dev/null +++ b/static.files/settings-74424d7eec62a23e.js @@ -0,0 +1,17 @@ +"use strict";(function(){const isSettingsPage=window.location.pathname.endsWith("/settings.html");function changeSetting(settingName,value){if(settingName==="theme"){const useSystem=value==="system preference"?"true":"false";updateLocalStorage("use-system-theme",useSystem)}updateLocalStorage(settingName,value);switch(settingName){case"theme":case"preferred-dark-theme":case"preferred-light-theme":updateTheme();updateLightAndDark();break;case"line-numbers":if(value===true){window.rustdoc_add_line_numbers_to_examples()}else{window.rustdoc_remove_line_numbers_from_examples()}break}}function showLightAndDark(){removeClass(document.getElementById("preferred-light-theme"),"hidden");removeClass(document.getElementById("preferred-dark-theme"),"hidden")}function hideLightAndDark(){addClass(document.getElementById("preferred-light-theme"),"hidden");addClass(document.getElementById("preferred-dark-theme"),"hidden")}function updateLightAndDark(){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||(useSystem===null&&getSettingValue("theme")===null)){showLightAndDark()}else{hideLightAndDark()}}function setEvents(settingsElement){updateLightAndDark();onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"),toggle=>{const settingId=toggle.id;const settingValue=getSettingValue(settingId);if(settingValue!==null){toggle.checked=settingValue==="true"}toggle.onchange=()=>{changeSetting(toggle.id,toggle.checked)}});onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"),elem=>{const settingId=elem.name;let settingValue=getSettingValue(settingId);if(settingId==="theme"){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||settingValue===null){settingValue=useSystem==="false"?"light":"system preference"}}if(settingValue!==null&&settingValue!=="null"){elem.checked=settingValue===elem.value}elem.addEventListener("change",ev=>{changeSetting(ev.target.name,ev.target.value)})})}function buildSettingsPageSections(settings){let output="";for(const setting of settings){const js_data_name=setting["js_name"];const setting_name=setting["name"];if(setting["options"]!==undefined){output+=`\ +
+
${setting_name}
+
`;onEach(setting["options"],option=>{const checked=option===setting["default"]?" checked":"";const full=`${js_data_name}-${option.replace(/ /g,"-")}`;output+=`\ + `});output+=`\ +
+
`}else{const checked=setting["default"]===true?" checked":"";output+=`\ +
\ + \ +
`}}return output}function buildSettingsPage(){const theme_names=getVar("themes").split(",").filter(t=>t);theme_names.push("light","dark","ayu");const settings=[{"name":"Theme","js_name":"theme","default":"system preference","options":theme_names.concat("system preference"),},{"name":"Preferred light theme","js_name":"preferred-light-theme","default":"light","options":theme_names,},{"name":"Preferred dark theme","js_name":"preferred-dark-theme","default":"dark","options":theme_names,},{"name":"Auto-hide item contents for large items","js_name":"auto-hide-large-items","default":true,},{"name":"Auto-hide item methods' documentation","js_name":"auto-hide-method-docs","default":false,},{"name":"Auto-hide trait implementation documentation","js_name":"auto-hide-trait-implementations","default":false,},{"name":"Directly go to item in search if there is only one result","js_name":"go-to-only-result","default":false,},{"name":"Show line numbers on code examples","js_name":"line-numbers","default":false,},{"name":"Disable keyboard shortcuts","js_name":"disable-shortcuts","default":false,},];const elementKind=isSettingsPage?"section":"div";const innerHTML=`
${buildSettingsPageSections(settings)}
`;const el=document.createElement(elementKind);el.id="settings";if(!isSettingsPage){el.className="popover"}el.innerHTML=innerHTML;if(isSettingsPage){document.getElementById(MAIN_ID).appendChild(el)}else{el.setAttribute("tabindex","-1");getSettingsButton().appendChild(el)}return el}const settingsMenu=buildSettingsPage();function displaySettings(){settingsMenu.style.display=""}function settingsBlurHandler(event){blurHandler(event,getSettingsButton(),window.hidePopoverMenus)}if(isSettingsPage){getSettingsButton().onclick=event=>{event.preventDefault()}}else{const settingsButton=getSettingsButton();const settingsMenu=document.getElementById("settings");settingsButton.onclick=event=>{if(elemIsInParent(event.target,settingsMenu)){return}event.preventDefault();const shouldDisplaySettings=settingsMenu.style.display==="none";window.hideAllModals();if(shouldDisplaySettings){displaySettings()}};settingsButton.onblur=settingsBlurHandler;settingsButton.querySelector("a").onblur=settingsBlurHandler;onEachLazy(settingsMenu.querySelectorAll("input"),el=>{el.onblur=settingsBlurHandler});settingsMenu.onblur=settingsBlurHandler}setTimeout(()=>{setEvents(settingsMenu);if(!isSettingsPage){displaySettings()}removeClass(getSettingsButton(),"rotate")},0)})() \ No newline at end of file diff --git a/static.files/src-script-3280b574d94e47b4.js b/static.files/src-script-3280b574d94e47b4.js new file mode 100644 index 0000000..9ea8892 --- /dev/null +++ b/static.files/src-script-3280b574d94e47b4.js @@ -0,0 +1 @@ +"use strict";(function(){const rootPath=getVar("root-path");const NAME_OFFSET=0;const DIRS_OFFSET=1;const FILES_OFFSET=2;const RUSTDOC_MOBILE_BREAKPOINT=700;function closeSidebarIfMobile(){if(window.innerWidth"){addClass(document.documentElement,"src-sidebar-expanded");child.innerText="<";updateLocalStorage("source-sidebar-show","true")}else{removeClass(document.documentElement,"src-sidebar-expanded");child.innerText=">";updateLocalStorage("source-sidebar-show","false")}}function createSidebarToggle(){const sidebarToggle=document.createElement("div");sidebarToggle.id="src-sidebar-toggle";const inner=document.createElement("button");if(getCurrentValue("source-sidebar-show")==="true"){inner.innerText="<"}else{inner.innerText=">"}inner.onclick=toggleSidebar;sidebarToggle.appendChild(inner);return sidebarToggle}function createSrcSidebar(){const container=document.querySelector("nav.sidebar");const sidebarToggle=createSidebarToggle();container.insertBefore(sidebarToggle,container.firstChild);const sidebar=document.createElement("div");sidebar.id="src-sidebar";let hasFoundFile=false;const title=document.createElement("div");title.className="title";title.innerText="Files";sidebar.appendChild(title);Object.keys(srcIndex).forEach(key=>{srcIndex[key][NAME_OFFSET]=key;hasFoundFile=createDirEntry(srcIndex[key],sidebar,"",hasFoundFile)});container.appendChild(sidebar);const selected_elem=sidebar.getElementsByClassName("selected")[0];if(typeof selected_elem!=="undefined"){selected_elem.focus()}}const lineNumbersRegex=/^#?(\d+)(?:-(\d+))?$/;function highlightSrcLines(match){if(typeof match==="undefined"){match=window.location.hash.match(lineNumbersRegex)}if(!match){return}let from=parseInt(match[1],10);let to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10)}if(to{onEachLazy(e.getElementsByTagName("a"),i_e=>{removeClass(i_e,"line-highlighted")})});for(let i=from;i<=to;++i){elem=document.getElementById(i);if(!elem){break}addClass(elem,"line-highlighted")}}const handleSrcHighlight=(function(){let prev_line_id=0;const set_fragment=name=>{const x=window.scrollX,y=window.scrollY;if(browserSupportsHistoryApi()){history.replaceState(null,null,"#"+name);highlightSrcLines()}else{location.replace("#"+name)}window.scrollTo(x,y)};return ev=>{let cur_line_id=parseInt(ev.target.id,10);if(isNaN(cur_line_id)||ev.ctrlKey||ev.altKey||ev.metaKey){return}ev.preventDefault();if(ev.shiftKey&&prev_line_id){if(prev_line_id>cur_line_id){const tmp=prev_line_id;prev_line_id=cur_line_id;cur_line_id=tmp}set_fragment(prev_line_id+"-"+cur_line_id)}else{prev_line_id=cur_line_id;set_fragment(cur_line_id)}}}());window.addEventListener("hashchange",()=>{const match=window.location.hash.match(lineNumbersRegex);if(match){return highlightSrcLines(match)}});onEachLazy(document.getElementsByClassName("src-line-numbers"),el=>{el.addEventListener("click",handleSrcHighlight)});highlightSrcLines();window.createSrcSidebar=createSrcSidebar})() \ No newline at end of file diff --git a/static.files/storage-fec3eaa3851e447d.js b/static.files/storage-fec3eaa3851e447d.js new file mode 100644 index 0000000..a687118 --- /dev/null +++ b/static.files/storage-fec3eaa3851e447d.js @@ -0,0 +1 @@ +"use strict";const builtinThemes=["light","dark","ayu"];const darkThemes=["dark","ayu"];window.currentTheme=document.getElementById("themeStyle");const settingsDataset=(function(){const settingsElement=document.getElementById("default-settings");return settingsElement&&settingsElement.dataset?settingsElement.dataset:null})();function getSettingValue(settingName){const current=getCurrentValue(settingName);if(current===null&&settingsDataset!==null){const def=settingsDataset[settingName.replace(/-/g,"_")];if(def!==undefined){return def}}return current}const localStoredTheme=getSettingValue("theme");function hasClass(elem,className){return elem&&elem.classList&&elem.classList.contains(className)}function addClass(elem,className){if(elem&&elem.classList){elem.classList.add(className)}}function removeClass(elem,className){if(elem&&elem.classList){elem.classList.remove(className)}}function onEach(arr,func,reversed){if(arr&&arr.length>0){if(reversed){for(let i=arr.length-1;i>=0;--i){if(func(arr[i])){return true}}}else{for(const elem of arr){if(func(elem)){return true}}}}return false}function onEachLazy(lazyArray,func,reversed){return onEach(Array.prototype.slice.call(lazyArray),func,reversed)}function updateLocalStorage(name,value){try{window.localStorage.setItem("rustdoc-"+name,value)}catch(e){}}function getCurrentValue(name){try{return window.localStorage.getItem("rustdoc-"+name)}catch(e){return null}}const getVar=(function getVar(name){const el=document.querySelector("head > meta[name='rustdoc-vars']");return el?el.attributes["data-"+name].value:null});function switchTheme(newThemeName,saveTheme){if(saveTheme){updateLocalStorage("theme",newThemeName)}document.documentElement.setAttribute("data-theme",newThemeName);if(builtinThemes.indexOf(newThemeName)!==-1){if(window.currentTheme){window.currentTheme.parentNode.removeChild(window.currentTheme);window.currentTheme=null}}else{const newHref=getVar("root-path")+newThemeName+getVar("resource-suffix")+".css";if(!window.currentTheme){if(document.readyState==="loading"){document.write(``);window.currentTheme=document.getElementById("themeStyle")}else{window.currentTheme=document.createElement("link");window.currentTheme.rel="stylesheet";window.currentTheme.id="themeStyle";window.currentTheme.href=newHref;document.documentElement.appendChild(window.currentTheme)}}else if(newHref!==window.currentTheme.href){window.currentTheme.href=newHref}}}const updateTheme=(function(){const mql=window.matchMedia("(prefers-color-scheme: dark)");function updateTheme(){if(getSettingValue("use-system-theme")!=="false"){const lightTheme=getSettingValue("preferred-light-theme")||"light";const darkTheme=getSettingValue("preferred-dark-theme")||"dark";updateLocalStorage("use-system-theme","true");switchTheme(mql.matches?darkTheme:lightTheme,true)}else{switchTheme(getSettingValue("theme"),false)}}mql.addEventListener("change",updateTheme);return updateTheme})();if(getSettingValue("use-system-theme")!=="false"&&window.matchMedia){if(getSettingValue("use-system-theme")===null&&getSettingValue("preferred-dark-theme")===null&&darkThemes.indexOf(localStoredTheme)>=0){updateLocalStorage("preferred-dark-theme",localStoredTheme)}}updateTheme();if(getSettingValue("source-sidebar-show")==="true"){addClass(document.documentElement,"src-sidebar-expanded")}window.addEventListener("pageshow",ev=>{if(ev.persisted){setTimeout(updateTheme,0)}}) \ No newline at end of file diff --git a/static.files/wheel-7b819b6101059cd0.svg b/static.files/wheel-7b819b6101059cd0.svg new file mode 100644 index 0000000..83c07f6 --- /dev/null +++ b/static.files/wheel-7b819b6101059cd0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/all.html b/test/all.html new file mode 100644 index 0000000..41f5a34 --- /dev/null +++ b/test/all.html @@ -0,0 +1 @@ +List of all items in this crate

List of all items

Functions

\ No newline at end of file diff --git a/test/fn.main.html b/test/fn.main.html new file mode 100644 index 0000000..0c6b2cc --- /dev/null +++ b/test/fn.main.html @@ -0,0 +1 @@ +main in test - Rust

Function test::main

source ·
pub(crate) fn main()
\ No newline at end of file diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..0d6bc18 --- /dev/null +++ b/test/index.html @@ -0,0 +1,8 @@ +test - Rust

Crate test

source ·
Expand description

Tests an Excel xlsx file. +Returns a list of differences in json format. +Saves an IronCalc version +This is primary for QA internal testing and will be superseded by an official +IronCalc CLI.

+

Usage: test file.xlsx

+

Functions

\ No newline at end of file diff --git a/test/sidebar-items.js b/test/sidebar-items.js new file mode 100644 index 0000000..9b556a0 --- /dev/null +++ b/test/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["main"]}; \ No newline at end of file diff --git a/trait.impl/core/clone/trait.Clone.js b/trait.impl/core/clone/trait.Clone.js new file mode 100644 index 0000000..91d141a --- /dev/null +++ b/trait.impl/core/clone/trait.Clone.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"ironcalc_base":[["impl Clone for DecimalFormats"],["impl Clone for Model"],["impl Clone for ParsedReference"],["impl Clone for Node"],["impl Clone for LexerMode"],["impl Clone for Dates"],["impl Clone for CellState"],["impl Clone for TableStyleInfo"],["impl Clone for CurrencyFormats"],["impl Clone for BorderStyle"],["impl Clone for TableSpecifier"],["impl Clone for ParsedDefinedName"],["impl Clone for Styles"],["impl Clone for Error"],["impl Clone for SheetState"],["impl Clone for HorizontalAlignment"],["impl Clone for FontScheme"],["impl Clone for NumbersSymbols"],["impl Clone for OpProduct"],["impl Clone for TableColumn"],["impl Clone for Fill"],["impl Clone for NumFmt"],["impl Clone for Border"],["impl Clone for WorkbookSettings"],["impl Clone for Worksheet"],["impl Clone for Row"],["impl Clone for TableReference"],["impl Clone for OpSum"],["impl Clone for VerticalAlignment"],["impl Clone for CellReferenceIndex"],["impl Clone for Col"],["impl Clone for Cell"],["impl Clone for OpUnary"],["impl Clone for Language"],["impl Clone for NumbersProperties"],["impl Clone for Table"],["impl Clone for Currency"],["impl Clone for CellStyles"],["impl Clone for OpCompare"],["impl Clone for LexerError"],["impl Clone for TokenType"],["impl Clone for Metadata"],["impl Clone for Alignment"],["impl Clone for Range"],["impl Clone for Locale"],["impl Clone for Booleans"],["impl Clone for BorderItem"],["impl Clone for Parser"],["impl Clone for CellReferenceRC"],["impl Clone for Style"],["impl Clone for DefinedName"],["impl Clone for Errors"],["impl Clone for Font"],["impl Clone for CellXfs"],["impl Clone for NavigationDirection"],["impl Clone for Comment"],["impl Clone for CellStyleXfs"],["impl Clone for Workbook"],["impl Clone for Lexer"],["impl Clone for CellReference"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/cmp/trait.Eq.js b/trait.impl/core/cmp/trait.Eq.js new file mode 100644 index 0000000..8aa67af --- /dev/null +++ b/trait.impl/core/cmp/trait.Eq.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl Eq for XlsxError"]], +"ironcalc_base":[["impl Eq for OpSum"],["impl Eq for HorizontalAlignment"],["impl Eq for SheetState"],["impl Eq for Style"],["impl Eq for Border"],["impl Eq for TableStyleInfo"],["impl Eq for CellXfs"],["impl Eq for WorksheetDimension"],["impl Eq for LexerError"],["impl Eq for Error"],["impl Eq for DefinedName"],["impl Eq for LexerMode"],["impl Eq for NavigationDirection"],["impl Eq for BorderStyle"],["impl Eq for Font"],["impl Eq for NumFmt"],["impl Eq for Comment"],["impl Eq for Styles"],["impl Eq for OpCompare"],["impl Eq for FontScheme"],["impl Eq for Table"],["impl Eq for TableColumn"],["impl Eq for CellType"],["impl Eq for VerticalAlignment"],["impl Eq for ParsedReference"],["impl Eq for OpUnary"],["impl Eq for CellReferenceIndex"],["impl Eq for CellReference"],["impl Eq for CellStyles"],["impl Eq for Alignment"],["impl Eq for Metadata"],["impl Eq for CellStyleXfs"],["impl Eq for Compare"],["impl Eq for OpProduct"],["impl Eq for BorderItem"],["impl Eq for Fill"],["impl Eq for WorkbookSettings"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/cmp/trait.PartialEq.js b/trait.impl/core/cmp/trait.PartialEq.js new file mode 100644 index 0000000..5c92b7a --- /dev/null +++ b/trait.impl/core/cmp/trait.PartialEq.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl PartialEq for XlsxError"]], +"ironcalc_base":[["impl PartialEq for Table"],["impl PartialEq for Font"],["impl PartialEq for HorizontalAlignment"],["impl PartialEq for Comment"],["impl PartialEq for OpCompare"],["impl PartialEq for ParsedReference"],["impl PartialEq for MarkedToken"],["impl PartialEq for CellXfs"],["impl PartialEq for LexerMode"],["impl PartialEq for BorderItem"],["impl PartialEq for WorksheetDimension"],["impl PartialEq for CellStyles"],["impl PartialEq for BorderStyle"],["impl PartialEq for Style"],["impl PartialEq for CellReference"],["impl PartialEq for Col"],["impl PartialEq for TableReference"],["impl PartialEq for Error"],["impl PartialEq for VerticalAlignment"],["impl PartialEq for TableColumn"],["impl PartialEq for NavigationDirection"],["impl PartialEq for Fill"],["impl PartialEq for Styles"],["impl PartialEq for Border"],["impl PartialEq for Compare"],["impl PartialEq for Node"],["impl PartialEq for CellReferenceIndex"],["impl PartialEq for Row"],["impl PartialEq for WorkbookSettings"],["impl PartialEq for Alignment"],["impl PartialEq for TableSpecifier"],["impl PartialEq for Workbook"],["impl PartialEq for Cell"],["impl PartialEq for TokenType"],["impl PartialEq for SheetState"],["impl PartialEq for FontScheme"],["impl PartialEq for DefinedName"],["impl PartialEq for LexerError"],["impl PartialEq for TableStyleInfo"],["impl PartialEq for OpUnary"],["impl PartialEq for SheetInfo"],["impl PartialEq for Token"],["impl PartialEq for OpSum"],["impl PartialEq for Metadata"],["impl PartialEq for CellValue"],["impl PartialEq for CellType"],["impl PartialEq for NumFmt"],["impl PartialEq for OpProduct"],["impl PartialEq for Worksheet"],["impl PartialEq for CellStyleXfs"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/convert/trait.From.js b/trait.impl/core/convert/trait.From.js new file mode 100644 index 0000000..35300a4 --- /dev/null +++ b/trait.impl/core/convert/trait.From.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl From<Error> for XlsxError"],["impl From<ZipError> for XlsxError"],["impl From<Error> for XlsxError"],["impl From<ParseFloatError> for XlsxError"],["impl From<ParseIntError> for XlsxError"]], +"ironcalc_base":[["impl From<bool> for CellValue"],["impl From<String> for CellValue"],["impl From<f64> for CellValue"],["impl From<&str> for CellValue"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/default/trait.Default.js b/trait.impl/core/default/trait.Default.js new file mode 100644 index 0000000..a4ccbc5 --- /dev/null +++ b/trait.impl/core/default/trait.Default.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"ironcalc_base":[["impl Default for HorizontalAlignment"],["impl Default for Cell"],["impl Default for CellStyles"],["impl Default for NumFmt"],["impl Default for Styles"],["impl Default for FontScheme"],["impl Default for Border"],["impl Default for TableColumn"],["impl Default for CellXfs"],["impl Default for CellStyleXfs"],["impl Default for Fill"],["impl Default for VerticalAlignment"],["impl Default for Font"],["impl Default for Alignment"],["impl Default for TableStyleInfo"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/error/trait.Error.js b/trait.impl/core/error/trait.Error.js new file mode 100644 index 0000000..76652af --- /dev/null +++ b/trait.impl/core/error/trait.Error.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"ironcalc":[["impl Error for XlsxError"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/fmt/trait.Debug.js b/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 0000000..abf5fb7 --- /dev/null +++ b/trait.impl/core/fmt/trait.Debug.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl Debug for XlsxError"]], +"ironcalc_base":[["impl Debug for Compare"],["impl Debug for OpSum"],["impl Debug for WorksheetDimension"],["impl Debug for FontScheme"],["impl Debug for CellStyles"],["impl Debug for Table"],["impl Debug for NumFmt"],["impl Debug for Border"],["impl Debug for ParsedReference"],["impl Debug for WorkbookSettings"],["impl Debug for TableStyleInfo"],["impl Debug for HorizontalAlignment"],["impl Debug for SheetInfo"],["impl Debug for CellXfs"],["impl Debug for Error"],["impl Debug for OpCompare"],["impl Debug for Fill"],["impl Debug for ParsedDefinedName"],["impl Debug for BorderStyle"],["impl Debug for Node"],["impl Debug for Col"],["impl Debug for TableReference"],["impl Debug for Worksheet"],["impl Debug for Row"],["impl Debug for CellReferenceIndex"],["impl Debug for Style"],["impl Debug for CellReference"],["impl Debug for Comment"],["impl Debug for SheetState"],["impl Debug for DefinedName"],["impl Debug for LexerError"],["impl Debug for Alignment"],["impl Debug for CellType"],["impl Debug for Token"],["impl Debug for CellValue"],["impl Debug for MarkedToken"],["impl Debug for TableSpecifier"],["impl Debug for BorderItem"],["impl Debug for OpProduct"],["impl Debug for Range"],["impl Debug for Metadata"],["impl Debug for VerticalAlignment"],["impl Debug for Workbook"],["impl Debug for Styles"],["impl Debug for CellStyleXfs"],["impl Debug for OpUnary"],["impl Debug for Font"],["impl Debug for Cell"],["impl Debug for TokenType"],["impl Debug for TableColumn"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/fmt/trait.Display.js b/trait.impl/core/fmt/trait.Display.js new file mode 100644 index 0000000..e8c90cc --- /dev/null +++ b/trait.impl/core/fmt/trait.Display.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl Display for XlsxError"]], +"ironcalc_base":[["impl Display for TokenType"],["impl Display for OpSum"],["impl Display for FontScheme"],["impl Display for VerticalAlignment"],["impl Display for OpProduct"],["impl Display for OpCompare"],["impl Display for BorderStyle"],["impl Display for Error"],["impl Display for HorizontalAlignment"],["impl Display for MarkedToken"],["impl Display for SheetState"],["impl Display for OpUnary"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Copy.js b/trait.impl/core/marker/trait.Copy.js new file mode 100644 index 0000000..40c6b7a --- /dev/null +++ b/trait.impl/core/marker/trait.Copy.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"ironcalc_base":[["impl Copy for CellReference"],["impl Copy for NavigationDirection"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Freeze.js b/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 0000000..5cf7039 --- /dev/null +++ b/trait.impl/core/marker/trait.Freeze.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl Freeze for CompareError",1,["ironcalc::compare::CompareError"]],["impl Freeze for Diff",1,["ironcalc::compare::Diff"]],["impl Freeze for XlsxError",1,["ironcalc::error::XlsxError"]]], +"ironcalc_base":[["impl Freeze for CellReference",1,["ironcalc_base::calc_result::CellReference"]],["impl Freeze for Range",1,["ironcalc_base::calc_result::Range"]],["impl Freeze for CellValue",1,["ironcalc_base::cell::CellValue"]],["impl Freeze for MarkedToken",1,["ironcalc_base::expressions::lexer::util::MarkedToken"]],["impl Freeze for LexerError",1,["ironcalc_base::expressions::lexer::LexerError"]],["impl Freeze for LexerMode",1,["ironcalc_base::expressions::lexer::LexerMode"]],["impl Freeze for Lexer",1,["ironcalc_base::expressions::lexer::Lexer"]],["impl Freeze for DisplaceData",1,["ironcalc_base::expressions::parser::stringify::DisplaceData"]],["impl Freeze for Node",1,["ironcalc_base::expressions::parser::Node"]],["impl Freeze for Parser",1,["ironcalc_base::expressions::parser::Parser"]],["impl Freeze for OpCompare",1,["ironcalc_base::expressions::token::OpCompare"]],["impl Freeze for OpUnary",1,["ironcalc_base::expressions::token::OpUnary"]],["impl Freeze for OpSum",1,["ironcalc_base::expressions::token::OpSum"]],["impl Freeze for OpProduct",1,["ironcalc_base::expressions::token::OpProduct"]],["impl Freeze for Error",1,["ironcalc_base::expressions::token::Error"]],["impl Freeze for TableSpecifier",1,["ironcalc_base::expressions::token::TableSpecifier"]],["impl Freeze for TableReference",1,["ironcalc_base::expressions::token::TableReference"]],["impl Freeze for TokenType",1,["ironcalc_base::expressions::token::TokenType"]],["impl Freeze for ParsedReference",1,["ironcalc_base::expressions::types::ParsedReference"]],["impl Freeze for ParsedRange",1,["ironcalc_base::expressions::types::ParsedRange"]],["impl Freeze for CellReference",1,["ironcalc_base::expressions::types::CellReference"]],["impl Freeze for CellReferenceRC",1,["ironcalc_base::expressions::types::CellReferenceRC"]],["impl Freeze for CellReferenceIndex",1,["ironcalc_base::expressions::types::CellReferenceIndex"]],["impl Freeze for Area",1,["ironcalc_base::expressions::types::Area"]],["impl Freeze for Formatted",1,["ironcalc_base::formatter::format::Formatted"]],["impl Freeze for Lexer",1,["ironcalc_base::formatter::lexer::Lexer"]],["impl Freeze for Token",1,["ironcalc_base::formatter::lexer::Token"]],["impl Freeze for Compare",1,["ironcalc_base::formatter::lexer::Compare"]],["impl Freeze for Digit",1,["ironcalc_base::formatter::parser::Digit"]],["impl Freeze for TextToken",1,["ironcalc_base::formatter::parser::TextToken"]],["impl Freeze for NumberPart",1,["ironcalc_base::formatter::parser::NumberPart"]],["impl Freeze for DatePart",1,["ironcalc_base::formatter::parser::DatePart"]],["impl Freeze for ErrorPart",1,["ironcalc_base::formatter::parser::ErrorPart"]],["impl Freeze for GeneralPart",1,["ironcalc_base::formatter::parser::GeneralPart"]],["impl Freeze for ParsePart",1,["ironcalc_base::formatter::parser::ParsePart"]],["impl Freeze for Parser",1,["ironcalc_base::formatter::parser::Parser"]],["impl Freeze for Booleans",1,["ironcalc_base::language::Booleans"]],["impl Freeze for Errors",1,["ironcalc_base::language::Errors"]],["impl Freeze for Language",1,["ironcalc_base::language::Language"]],["impl Freeze for Locale",1,["ironcalc_base::locale::Locale"]],["impl Freeze for Currency",1,["ironcalc_base::locale::Currency"]],["impl Freeze for NumbersProperties",1,["ironcalc_base::locale::NumbersProperties"]],["impl Freeze for Dates",1,["ironcalc_base::locale::Dates"]],["impl Freeze for NumbersSymbols",1,["ironcalc_base::locale::NumbersSymbols"]],["impl Freeze for CurrencyFormats",1,["ironcalc_base::locale::CurrencyFormats"]],["impl Freeze for DecimalFormats",1,["ironcalc_base::locale::DecimalFormats"]],["impl Freeze for CellState",1,["ironcalc_base::model::CellState"]],["impl Freeze for ParsedDefinedName",1,["ironcalc_base::model::ParsedDefinedName"]],["impl Freeze for Model",1,["ironcalc_base::model::Model"]],["impl Freeze for CellIndex",1,["ironcalc_base::model::CellIndex"]],["impl Freeze for Style",1,["ironcalc_base::model::Style"]],["impl Freeze for Metadata",1,["ironcalc_base::types::Metadata"]],["impl Freeze for WorkbookSettings",1,["ironcalc_base::types::WorkbookSettings"]],["impl Freeze for Workbook",1,["ironcalc_base::types::Workbook"]],["impl Freeze for DefinedName",1,["ironcalc_base::types::DefinedName"]],["impl Freeze for SheetState",1,["ironcalc_base::types::SheetState"]],["impl Freeze for Worksheet",1,["ironcalc_base::types::Worksheet"]],["impl Freeze for Row",1,["ironcalc_base::types::Row"]],["impl Freeze for Col",1,["ironcalc_base::types::Col"]],["impl Freeze for CellType",1,["ironcalc_base::types::CellType"]],["impl Freeze for Cell",1,["ironcalc_base::types::Cell"]],["impl Freeze for Comment",1,["ironcalc_base::types::Comment"]],["impl Freeze for Table",1,["ironcalc_base::types::Table"]],["impl Freeze for TableColumn",1,["ironcalc_base::types::TableColumn"]],["impl Freeze for TableStyleInfo",1,["ironcalc_base::types::TableStyleInfo"]],["impl Freeze for Styles",1,["ironcalc_base::types::Styles"]],["impl Freeze for NumFmt",1,["ironcalc_base::types::NumFmt"]],["impl Freeze for FontScheme",1,["ironcalc_base::types::FontScheme"]],["impl Freeze for Font",1,["ironcalc_base::types::Font"]],["impl Freeze for Fill",1,["ironcalc_base::types::Fill"]],["impl Freeze for HorizontalAlignment",1,["ironcalc_base::types::HorizontalAlignment"]],["impl Freeze for VerticalAlignment",1,["ironcalc_base::types::VerticalAlignment"]],["impl Freeze for Alignment",1,["ironcalc_base::types::Alignment"]],["impl Freeze for CellStyleXfs",1,["ironcalc_base::types::CellStyleXfs"]],["impl Freeze for CellXfs",1,["ironcalc_base::types::CellXfs"]],["impl Freeze for CellStyles",1,["ironcalc_base::types::CellStyles"]],["impl Freeze for BorderStyle",1,["ironcalc_base::types::BorderStyle"]],["impl Freeze for BorderItem",1,["ironcalc_base::types::BorderItem"]],["impl Freeze for Border",1,["ironcalc_base::types::Border"]],["impl Freeze for SheetInfo",1,["ironcalc_base::types::SheetInfo"]],["impl Freeze for WorksheetDimension",1,["ironcalc_base::worksheet::WorksheetDimension"]],["impl Freeze for NavigationDirection",1,["ironcalc_base::worksheet::NavigationDirection"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Send.js b/trait.impl/core/marker/trait.Send.js new file mode 100644 index 0000000..fe69649 --- /dev/null +++ b/trait.impl/core/marker/trait.Send.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl Send for CompareError",1,["ironcalc::compare::CompareError"]],["impl Send for Diff",1,["ironcalc::compare::Diff"]],["impl Send for XlsxError",1,["ironcalc::error::XlsxError"]]], +"ironcalc_base":[["impl Send for CellReference",1,["ironcalc_base::calc_result::CellReference"]],["impl Send for Range",1,["ironcalc_base::calc_result::Range"]],["impl Send for CellValue",1,["ironcalc_base::cell::CellValue"]],["impl Send for MarkedToken",1,["ironcalc_base::expressions::lexer::util::MarkedToken"]],["impl Send for LexerError",1,["ironcalc_base::expressions::lexer::LexerError"]],["impl Send for LexerMode",1,["ironcalc_base::expressions::lexer::LexerMode"]],["impl Send for Lexer",1,["ironcalc_base::expressions::lexer::Lexer"]],["impl Send for DisplaceData",1,["ironcalc_base::expressions::parser::stringify::DisplaceData"]],["impl Send for Node",1,["ironcalc_base::expressions::parser::Node"]],["impl Send for Parser",1,["ironcalc_base::expressions::parser::Parser"]],["impl Send for OpCompare",1,["ironcalc_base::expressions::token::OpCompare"]],["impl Send for OpUnary",1,["ironcalc_base::expressions::token::OpUnary"]],["impl Send for OpSum",1,["ironcalc_base::expressions::token::OpSum"]],["impl Send for OpProduct",1,["ironcalc_base::expressions::token::OpProduct"]],["impl Send for Error",1,["ironcalc_base::expressions::token::Error"]],["impl Send for TableSpecifier",1,["ironcalc_base::expressions::token::TableSpecifier"]],["impl Send for TableReference",1,["ironcalc_base::expressions::token::TableReference"]],["impl Send for TokenType",1,["ironcalc_base::expressions::token::TokenType"]],["impl Send for ParsedReference",1,["ironcalc_base::expressions::types::ParsedReference"]],["impl Send for ParsedRange",1,["ironcalc_base::expressions::types::ParsedRange"]],["impl Send for CellReference",1,["ironcalc_base::expressions::types::CellReference"]],["impl Send for CellReferenceRC",1,["ironcalc_base::expressions::types::CellReferenceRC"]],["impl Send for CellReferenceIndex",1,["ironcalc_base::expressions::types::CellReferenceIndex"]],["impl Send for Area",1,["ironcalc_base::expressions::types::Area"]],["impl Send for Formatted",1,["ironcalc_base::formatter::format::Formatted"]],["impl Send for Lexer",1,["ironcalc_base::formatter::lexer::Lexer"]],["impl Send for Token",1,["ironcalc_base::formatter::lexer::Token"]],["impl Send for Compare",1,["ironcalc_base::formatter::lexer::Compare"]],["impl Send for Digit",1,["ironcalc_base::formatter::parser::Digit"]],["impl Send for TextToken",1,["ironcalc_base::formatter::parser::TextToken"]],["impl Send for NumberPart",1,["ironcalc_base::formatter::parser::NumberPart"]],["impl Send for DatePart",1,["ironcalc_base::formatter::parser::DatePart"]],["impl Send for ErrorPart",1,["ironcalc_base::formatter::parser::ErrorPart"]],["impl Send for GeneralPart",1,["ironcalc_base::formatter::parser::GeneralPart"]],["impl Send for ParsePart",1,["ironcalc_base::formatter::parser::ParsePart"]],["impl Send for Parser",1,["ironcalc_base::formatter::parser::Parser"]],["impl Send for Booleans",1,["ironcalc_base::language::Booleans"]],["impl Send for Errors",1,["ironcalc_base::language::Errors"]],["impl Send for Language",1,["ironcalc_base::language::Language"]],["impl Send for Locale",1,["ironcalc_base::locale::Locale"]],["impl Send for Currency",1,["ironcalc_base::locale::Currency"]],["impl Send for NumbersProperties",1,["ironcalc_base::locale::NumbersProperties"]],["impl Send for Dates",1,["ironcalc_base::locale::Dates"]],["impl Send for NumbersSymbols",1,["ironcalc_base::locale::NumbersSymbols"]],["impl Send for CurrencyFormats",1,["ironcalc_base::locale::CurrencyFormats"]],["impl Send for DecimalFormats",1,["ironcalc_base::locale::DecimalFormats"]],["impl Send for CellState",1,["ironcalc_base::model::CellState"]],["impl Send for ParsedDefinedName",1,["ironcalc_base::model::ParsedDefinedName"]],["impl Send for Model",1,["ironcalc_base::model::Model"]],["impl Send for CellIndex",1,["ironcalc_base::model::CellIndex"]],["impl Send for Style",1,["ironcalc_base::model::Style"]],["impl Send for Metadata",1,["ironcalc_base::types::Metadata"]],["impl Send for WorkbookSettings",1,["ironcalc_base::types::WorkbookSettings"]],["impl Send for Workbook",1,["ironcalc_base::types::Workbook"]],["impl Send for DefinedName",1,["ironcalc_base::types::DefinedName"]],["impl Send for SheetState",1,["ironcalc_base::types::SheetState"]],["impl Send for Worksheet",1,["ironcalc_base::types::Worksheet"]],["impl Send for Row",1,["ironcalc_base::types::Row"]],["impl Send for Col",1,["ironcalc_base::types::Col"]],["impl Send for CellType",1,["ironcalc_base::types::CellType"]],["impl Send for Cell",1,["ironcalc_base::types::Cell"]],["impl Send for Comment",1,["ironcalc_base::types::Comment"]],["impl Send for Table",1,["ironcalc_base::types::Table"]],["impl Send for TableColumn",1,["ironcalc_base::types::TableColumn"]],["impl Send for TableStyleInfo",1,["ironcalc_base::types::TableStyleInfo"]],["impl Send for Styles",1,["ironcalc_base::types::Styles"]],["impl Send for NumFmt",1,["ironcalc_base::types::NumFmt"]],["impl Send for FontScheme",1,["ironcalc_base::types::FontScheme"]],["impl Send for Font",1,["ironcalc_base::types::Font"]],["impl Send for Fill",1,["ironcalc_base::types::Fill"]],["impl Send for HorizontalAlignment",1,["ironcalc_base::types::HorizontalAlignment"]],["impl Send for VerticalAlignment",1,["ironcalc_base::types::VerticalAlignment"]],["impl Send for Alignment",1,["ironcalc_base::types::Alignment"]],["impl Send for CellStyleXfs",1,["ironcalc_base::types::CellStyleXfs"]],["impl Send for CellXfs",1,["ironcalc_base::types::CellXfs"]],["impl Send for CellStyles",1,["ironcalc_base::types::CellStyles"]],["impl Send for BorderStyle",1,["ironcalc_base::types::BorderStyle"]],["impl Send for BorderItem",1,["ironcalc_base::types::BorderItem"]],["impl Send for Border",1,["ironcalc_base::types::Border"]],["impl Send for SheetInfo",1,["ironcalc_base::types::SheetInfo"]],["impl Send for WorksheetDimension",1,["ironcalc_base::worksheet::WorksheetDimension"]],["impl Send for NavigationDirection",1,["ironcalc_base::worksheet::NavigationDirection"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.StructuralEq.js b/trait.impl/core/marker/trait.StructuralEq.js new file mode 100644 index 0000000..9a727a7 --- /dev/null +++ b/trait.impl/core/marker/trait.StructuralEq.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl StructuralEq for XlsxError"]], +"ironcalc_base":[["impl StructuralEq for FontScheme"],["impl StructuralEq for CellReference"],["impl StructuralEq for Metadata"],["impl StructuralEq for DefinedName"],["impl StructuralEq for WorkbookSettings"],["impl StructuralEq for TableColumn"],["impl StructuralEq for Compare"],["impl StructuralEq for Fill"],["impl StructuralEq for HorizontalAlignment"],["impl StructuralEq for WorksheetDimension"],["impl StructuralEq for OpCompare"],["impl StructuralEq for CellReferenceIndex"],["impl StructuralEq for Border"],["impl StructuralEq for Style"],["impl StructuralEq for BorderStyle"],["impl StructuralEq for CellStyles"],["impl StructuralEq for Styles"],["impl StructuralEq for LexerError"],["impl StructuralEq for OpUnary"],["impl StructuralEq for BorderItem"],["impl StructuralEq for TableStyleInfo"],["impl StructuralEq for OpSum"],["impl StructuralEq for CellXfs"],["impl StructuralEq for Comment"],["impl StructuralEq for LexerMode"],["impl StructuralEq for Error"],["impl StructuralEq for CellType"],["impl StructuralEq for ParsedReference"],["impl StructuralEq for NumFmt"],["impl StructuralEq for OpProduct"],["impl StructuralEq for NavigationDirection"],["impl StructuralEq for SheetState"],["impl StructuralEq for Table"],["impl StructuralEq for Alignment"],["impl StructuralEq for CellStyleXfs"],["impl StructuralEq for VerticalAlignment"],["impl StructuralEq for Font"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.StructuralPartialEq.js b/trait.impl/core/marker/trait.StructuralPartialEq.js new file mode 100644 index 0000000..d4dc9ef --- /dev/null +++ b/trait.impl/core/marker/trait.StructuralPartialEq.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl StructuralPartialEq for XlsxError"]], +"ironcalc_base":[["impl StructuralPartialEq for Error"],["impl StructuralPartialEq for OpProduct"],["impl StructuralPartialEq for Cell"],["impl StructuralPartialEq for CellReference"],["impl StructuralPartialEq for TableSpecifier"],["impl StructuralPartialEq for Fill"],["impl StructuralPartialEq for LexerMode"],["impl StructuralPartialEq for CellReferenceIndex"],["impl StructuralPartialEq for Border"],["impl StructuralPartialEq for NumFmt"],["impl StructuralPartialEq for BorderStyle"],["impl StructuralPartialEq for LexerError"],["impl StructuralPartialEq for TableStyleInfo"],["impl StructuralPartialEq for OpSum"],["impl StructuralPartialEq for CellXfs"],["impl StructuralPartialEq for Node"],["impl StructuralPartialEq for WorksheetDimension"],["impl StructuralPartialEq for OpUnary"],["impl StructuralPartialEq for TableColumn"],["impl StructuralPartialEq for BorderItem"],["impl StructuralPartialEq for SheetState"],["impl StructuralPartialEq for NavigationDirection"],["impl StructuralPartialEq for CellType"],["impl StructuralPartialEq for MarkedToken"],["impl StructuralPartialEq for Col"],["impl StructuralPartialEq for Font"],["impl StructuralPartialEq for OpCompare"],["impl StructuralPartialEq for FontScheme"],["impl StructuralPartialEq for Compare"],["impl StructuralPartialEq for Alignment"],["impl StructuralPartialEq for DefinedName"],["impl StructuralPartialEq for Table"],["impl StructuralPartialEq for VerticalAlignment"],["impl StructuralPartialEq for Metadata"],["impl StructuralPartialEq for WorkbookSettings"],["impl StructuralPartialEq for CellValue"],["impl StructuralPartialEq for Token"],["impl StructuralPartialEq for Worksheet"],["impl StructuralPartialEq for Comment"],["impl StructuralPartialEq for Row"],["impl StructuralPartialEq for TokenType"],["impl StructuralPartialEq for ParsedReference"],["impl StructuralPartialEq for Styles"],["impl StructuralPartialEq for Style"],["impl StructuralPartialEq for HorizontalAlignment"],["impl StructuralPartialEq for CellStyles"],["impl StructuralPartialEq for Workbook"],["impl StructuralPartialEq for TableReference"],["impl StructuralPartialEq for SheetInfo"],["impl StructuralPartialEq for CellStyleXfs"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Sync.js b/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 0000000..26914fa --- /dev/null +++ b/trait.impl/core/marker/trait.Sync.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl Sync for CompareError",1,["ironcalc::compare::CompareError"]],["impl Sync for Diff",1,["ironcalc::compare::Diff"]],["impl Sync for XlsxError",1,["ironcalc::error::XlsxError"]]], +"ironcalc_base":[["impl Sync for CellReference",1,["ironcalc_base::calc_result::CellReference"]],["impl Sync for Range",1,["ironcalc_base::calc_result::Range"]],["impl Sync for CellValue",1,["ironcalc_base::cell::CellValue"]],["impl Sync for MarkedToken",1,["ironcalc_base::expressions::lexer::util::MarkedToken"]],["impl Sync for LexerError",1,["ironcalc_base::expressions::lexer::LexerError"]],["impl Sync for LexerMode",1,["ironcalc_base::expressions::lexer::LexerMode"]],["impl Sync for Lexer",1,["ironcalc_base::expressions::lexer::Lexer"]],["impl Sync for DisplaceData",1,["ironcalc_base::expressions::parser::stringify::DisplaceData"]],["impl Sync for Node",1,["ironcalc_base::expressions::parser::Node"]],["impl Sync for Parser",1,["ironcalc_base::expressions::parser::Parser"]],["impl Sync for OpCompare",1,["ironcalc_base::expressions::token::OpCompare"]],["impl Sync for OpUnary",1,["ironcalc_base::expressions::token::OpUnary"]],["impl Sync for OpSum",1,["ironcalc_base::expressions::token::OpSum"]],["impl Sync for OpProduct",1,["ironcalc_base::expressions::token::OpProduct"]],["impl Sync for Error",1,["ironcalc_base::expressions::token::Error"]],["impl Sync for TableSpecifier",1,["ironcalc_base::expressions::token::TableSpecifier"]],["impl Sync for TableReference",1,["ironcalc_base::expressions::token::TableReference"]],["impl Sync for TokenType",1,["ironcalc_base::expressions::token::TokenType"]],["impl Sync for ParsedReference",1,["ironcalc_base::expressions::types::ParsedReference"]],["impl Sync for ParsedRange",1,["ironcalc_base::expressions::types::ParsedRange"]],["impl Sync for CellReference",1,["ironcalc_base::expressions::types::CellReference"]],["impl Sync for CellReferenceRC",1,["ironcalc_base::expressions::types::CellReferenceRC"]],["impl Sync for CellReferenceIndex",1,["ironcalc_base::expressions::types::CellReferenceIndex"]],["impl Sync for Area",1,["ironcalc_base::expressions::types::Area"]],["impl Sync for Formatted",1,["ironcalc_base::formatter::format::Formatted"]],["impl Sync for Lexer",1,["ironcalc_base::formatter::lexer::Lexer"]],["impl Sync for Token",1,["ironcalc_base::formatter::lexer::Token"]],["impl Sync for Compare",1,["ironcalc_base::formatter::lexer::Compare"]],["impl Sync for Digit",1,["ironcalc_base::formatter::parser::Digit"]],["impl Sync for TextToken",1,["ironcalc_base::formatter::parser::TextToken"]],["impl Sync for NumberPart",1,["ironcalc_base::formatter::parser::NumberPart"]],["impl Sync for DatePart",1,["ironcalc_base::formatter::parser::DatePart"]],["impl Sync for ErrorPart",1,["ironcalc_base::formatter::parser::ErrorPart"]],["impl Sync for GeneralPart",1,["ironcalc_base::formatter::parser::GeneralPart"]],["impl Sync for ParsePart",1,["ironcalc_base::formatter::parser::ParsePart"]],["impl Sync for Parser",1,["ironcalc_base::formatter::parser::Parser"]],["impl Sync for Booleans",1,["ironcalc_base::language::Booleans"]],["impl Sync for Errors",1,["ironcalc_base::language::Errors"]],["impl Sync for Language",1,["ironcalc_base::language::Language"]],["impl Sync for Locale",1,["ironcalc_base::locale::Locale"]],["impl Sync for Currency",1,["ironcalc_base::locale::Currency"]],["impl Sync for NumbersProperties",1,["ironcalc_base::locale::NumbersProperties"]],["impl Sync for Dates",1,["ironcalc_base::locale::Dates"]],["impl Sync for NumbersSymbols",1,["ironcalc_base::locale::NumbersSymbols"]],["impl Sync for CurrencyFormats",1,["ironcalc_base::locale::CurrencyFormats"]],["impl Sync for DecimalFormats",1,["ironcalc_base::locale::DecimalFormats"]],["impl Sync for CellState",1,["ironcalc_base::model::CellState"]],["impl Sync for ParsedDefinedName",1,["ironcalc_base::model::ParsedDefinedName"]],["impl Sync for Model",1,["ironcalc_base::model::Model"]],["impl Sync for CellIndex",1,["ironcalc_base::model::CellIndex"]],["impl Sync for Style",1,["ironcalc_base::model::Style"]],["impl Sync for Metadata",1,["ironcalc_base::types::Metadata"]],["impl Sync for WorkbookSettings",1,["ironcalc_base::types::WorkbookSettings"]],["impl Sync for Workbook",1,["ironcalc_base::types::Workbook"]],["impl Sync for DefinedName",1,["ironcalc_base::types::DefinedName"]],["impl Sync for SheetState",1,["ironcalc_base::types::SheetState"]],["impl Sync for Worksheet",1,["ironcalc_base::types::Worksheet"]],["impl Sync for Row",1,["ironcalc_base::types::Row"]],["impl Sync for Col",1,["ironcalc_base::types::Col"]],["impl Sync for CellType",1,["ironcalc_base::types::CellType"]],["impl Sync for Cell",1,["ironcalc_base::types::Cell"]],["impl Sync for Comment",1,["ironcalc_base::types::Comment"]],["impl Sync for Table",1,["ironcalc_base::types::Table"]],["impl Sync for TableColumn",1,["ironcalc_base::types::TableColumn"]],["impl Sync for TableStyleInfo",1,["ironcalc_base::types::TableStyleInfo"]],["impl Sync for Styles",1,["ironcalc_base::types::Styles"]],["impl Sync for NumFmt",1,["ironcalc_base::types::NumFmt"]],["impl Sync for FontScheme",1,["ironcalc_base::types::FontScheme"]],["impl Sync for Font",1,["ironcalc_base::types::Font"]],["impl Sync for Fill",1,["ironcalc_base::types::Fill"]],["impl Sync for HorizontalAlignment",1,["ironcalc_base::types::HorizontalAlignment"]],["impl Sync for VerticalAlignment",1,["ironcalc_base::types::VerticalAlignment"]],["impl Sync for Alignment",1,["ironcalc_base::types::Alignment"]],["impl Sync for CellStyleXfs",1,["ironcalc_base::types::CellStyleXfs"]],["impl Sync for CellXfs",1,["ironcalc_base::types::CellXfs"]],["impl Sync for CellStyles",1,["ironcalc_base::types::CellStyles"]],["impl Sync for BorderStyle",1,["ironcalc_base::types::BorderStyle"]],["impl Sync for BorderItem",1,["ironcalc_base::types::BorderItem"]],["impl Sync for Border",1,["ironcalc_base::types::Border"]],["impl Sync for SheetInfo",1,["ironcalc_base::types::SheetInfo"]],["impl Sync for WorksheetDimension",1,["ironcalc_base::worksheet::WorksheetDimension"]],["impl Sync for NavigationDirection",1,["ironcalc_base::worksheet::NavigationDirection"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Unpin.js b/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 0000000..d23d34d --- /dev/null +++ b/trait.impl/core/marker/trait.Unpin.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl Unpin for CompareError",1,["ironcalc::compare::CompareError"]],["impl Unpin for Diff",1,["ironcalc::compare::Diff"]],["impl Unpin for XlsxError",1,["ironcalc::error::XlsxError"]]], +"ironcalc_base":[["impl Unpin for CellReference",1,["ironcalc_base::calc_result::CellReference"]],["impl Unpin for Range",1,["ironcalc_base::calc_result::Range"]],["impl Unpin for CellValue",1,["ironcalc_base::cell::CellValue"]],["impl Unpin for MarkedToken",1,["ironcalc_base::expressions::lexer::util::MarkedToken"]],["impl Unpin for LexerError",1,["ironcalc_base::expressions::lexer::LexerError"]],["impl Unpin for LexerMode",1,["ironcalc_base::expressions::lexer::LexerMode"]],["impl Unpin for Lexer",1,["ironcalc_base::expressions::lexer::Lexer"]],["impl Unpin for DisplaceData",1,["ironcalc_base::expressions::parser::stringify::DisplaceData"]],["impl Unpin for Node",1,["ironcalc_base::expressions::parser::Node"]],["impl Unpin for Parser",1,["ironcalc_base::expressions::parser::Parser"]],["impl Unpin for OpCompare",1,["ironcalc_base::expressions::token::OpCompare"]],["impl Unpin for OpUnary",1,["ironcalc_base::expressions::token::OpUnary"]],["impl Unpin for OpSum",1,["ironcalc_base::expressions::token::OpSum"]],["impl Unpin for OpProduct",1,["ironcalc_base::expressions::token::OpProduct"]],["impl Unpin for Error",1,["ironcalc_base::expressions::token::Error"]],["impl Unpin for TableSpecifier",1,["ironcalc_base::expressions::token::TableSpecifier"]],["impl Unpin for TableReference",1,["ironcalc_base::expressions::token::TableReference"]],["impl Unpin for TokenType",1,["ironcalc_base::expressions::token::TokenType"]],["impl Unpin for ParsedReference",1,["ironcalc_base::expressions::types::ParsedReference"]],["impl Unpin for ParsedRange",1,["ironcalc_base::expressions::types::ParsedRange"]],["impl Unpin for CellReference",1,["ironcalc_base::expressions::types::CellReference"]],["impl Unpin for CellReferenceRC",1,["ironcalc_base::expressions::types::CellReferenceRC"]],["impl Unpin for CellReferenceIndex",1,["ironcalc_base::expressions::types::CellReferenceIndex"]],["impl Unpin for Area",1,["ironcalc_base::expressions::types::Area"]],["impl Unpin for Formatted",1,["ironcalc_base::formatter::format::Formatted"]],["impl Unpin for Lexer",1,["ironcalc_base::formatter::lexer::Lexer"]],["impl Unpin for Token",1,["ironcalc_base::formatter::lexer::Token"]],["impl Unpin for Compare",1,["ironcalc_base::formatter::lexer::Compare"]],["impl Unpin for Digit",1,["ironcalc_base::formatter::parser::Digit"]],["impl Unpin for TextToken",1,["ironcalc_base::formatter::parser::TextToken"]],["impl Unpin for NumberPart",1,["ironcalc_base::formatter::parser::NumberPart"]],["impl Unpin for DatePart",1,["ironcalc_base::formatter::parser::DatePart"]],["impl Unpin for ErrorPart",1,["ironcalc_base::formatter::parser::ErrorPart"]],["impl Unpin for GeneralPart",1,["ironcalc_base::formatter::parser::GeneralPart"]],["impl Unpin for ParsePart",1,["ironcalc_base::formatter::parser::ParsePart"]],["impl Unpin for Parser",1,["ironcalc_base::formatter::parser::Parser"]],["impl Unpin for Booleans",1,["ironcalc_base::language::Booleans"]],["impl Unpin for Errors",1,["ironcalc_base::language::Errors"]],["impl Unpin for Language",1,["ironcalc_base::language::Language"]],["impl Unpin for Locale",1,["ironcalc_base::locale::Locale"]],["impl Unpin for Currency",1,["ironcalc_base::locale::Currency"]],["impl Unpin for NumbersProperties",1,["ironcalc_base::locale::NumbersProperties"]],["impl Unpin for Dates",1,["ironcalc_base::locale::Dates"]],["impl Unpin for NumbersSymbols",1,["ironcalc_base::locale::NumbersSymbols"]],["impl Unpin for CurrencyFormats",1,["ironcalc_base::locale::CurrencyFormats"]],["impl Unpin for DecimalFormats",1,["ironcalc_base::locale::DecimalFormats"]],["impl Unpin for CellState",1,["ironcalc_base::model::CellState"]],["impl Unpin for ParsedDefinedName",1,["ironcalc_base::model::ParsedDefinedName"]],["impl Unpin for Model",1,["ironcalc_base::model::Model"]],["impl Unpin for CellIndex",1,["ironcalc_base::model::CellIndex"]],["impl Unpin for Style",1,["ironcalc_base::model::Style"]],["impl Unpin for Metadata",1,["ironcalc_base::types::Metadata"]],["impl Unpin for WorkbookSettings",1,["ironcalc_base::types::WorkbookSettings"]],["impl Unpin for Workbook",1,["ironcalc_base::types::Workbook"]],["impl Unpin for DefinedName",1,["ironcalc_base::types::DefinedName"]],["impl Unpin for SheetState",1,["ironcalc_base::types::SheetState"]],["impl Unpin for Worksheet",1,["ironcalc_base::types::Worksheet"]],["impl Unpin for Row",1,["ironcalc_base::types::Row"]],["impl Unpin for Col",1,["ironcalc_base::types::Col"]],["impl Unpin for CellType",1,["ironcalc_base::types::CellType"]],["impl Unpin for Cell",1,["ironcalc_base::types::Cell"]],["impl Unpin for Comment",1,["ironcalc_base::types::Comment"]],["impl Unpin for Table",1,["ironcalc_base::types::Table"]],["impl Unpin for TableColumn",1,["ironcalc_base::types::TableColumn"]],["impl Unpin for TableStyleInfo",1,["ironcalc_base::types::TableStyleInfo"]],["impl Unpin for Styles",1,["ironcalc_base::types::Styles"]],["impl Unpin for NumFmt",1,["ironcalc_base::types::NumFmt"]],["impl Unpin for FontScheme",1,["ironcalc_base::types::FontScheme"]],["impl Unpin for Font",1,["ironcalc_base::types::Font"]],["impl Unpin for Fill",1,["ironcalc_base::types::Fill"]],["impl Unpin for HorizontalAlignment",1,["ironcalc_base::types::HorizontalAlignment"]],["impl Unpin for VerticalAlignment",1,["ironcalc_base::types::VerticalAlignment"]],["impl Unpin for Alignment",1,["ironcalc_base::types::Alignment"]],["impl Unpin for CellStyleXfs",1,["ironcalc_base::types::CellStyleXfs"]],["impl Unpin for CellXfs",1,["ironcalc_base::types::CellXfs"]],["impl Unpin for CellStyles",1,["ironcalc_base::types::CellStyles"]],["impl Unpin for BorderStyle",1,["ironcalc_base::types::BorderStyle"]],["impl Unpin for BorderItem",1,["ironcalc_base::types::BorderItem"]],["impl Unpin for Border",1,["ironcalc_base::types::Border"]],["impl Unpin for SheetInfo",1,["ironcalc_base::types::SheetInfo"]],["impl Unpin for WorksheetDimension",1,["ironcalc_base::worksheet::WorksheetDimension"]],["impl Unpin for NavigationDirection",1,["ironcalc_base::worksheet::NavigationDirection"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 0000000..d7cb961 --- /dev/null +++ b/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl RefUnwindSafe for CompareError",1,["ironcalc::compare::CompareError"]],["impl RefUnwindSafe for Diff",1,["ironcalc::compare::Diff"]],["impl RefUnwindSafe for XlsxError",1,["ironcalc::error::XlsxError"]]], +"ironcalc_base":[["impl RefUnwindSafe for CellReference",1,["ironcalc_base::calc_result::CellReference"]],["impl RefUnwindSafe for Range",1,["ironcalc_base::calc_result::Range"]],["impl RefUnwindSafe for CellValue",1,["ironcalc_base::cell::CellValue"]],["impl RefUnwindSafe for MarkedToken",1,["ironcalc_base::expressions::lexer::util::MarkedToken"]],["impl RefUnwindSafe for LexerError",1,["ironcalc_base::expressions::lexer::LexerError"]],["impl RefUnwindSafe for LexerMode",1,["ironcalc_base::expressions::lexer::LexerMode"]],["impl RefUnwindSafe for Lexer",1,["ironcalc_base::expressions::lexer::Lexer"]],["impl RefUnwindSafe for DisplaceData",1,["ironcalc_base::expressions::parser::stringify::DisplaceData"]],["impl RefUnwindSafe for Node",1,["ironcalc_base::expressions::parser::Node"]],["impl RefUnwindSafe for Parser",1,["ironcalc_base::expressions::parser::Parser"]],["impl RefUnwindSafe for OpCompare",1,["ironcalc_base::expressions::token::OpCompare"]],["impl RefUnwindSafe for OpUnary",1,["ironcalc_base::expressions::token::OpUnary"]],["impl RefUnwindSafe for OpSum",1,["ironcalc_base::expressions::token::OpSum"]],["impl RefUnwindSafe for OpProduct",1,["ironcalc_base::expressions::token::OpProduct"]],["impl RefUnwindSafe for Error",1,["ironcalc_base::expressions::token::Error"]],["impl RefUnwindSafe for TableSpecifier",1,["ironcalc_base::expressions::token::TableSpecifier"]],["impl RefUnwindSafe for TableReference",1,["ironcalc_base::expressions::token::TableReference"]],["impl RefUnwindSafe for TokenType",1,["ironcalc_base::expressions::token::TokenType"]],["impl RefUnwindSafe for ParsedReference",1,["ironcalc_base::expressions::types::ParsedReference"]],["impl RefUnwindSafe for ParsedRange",1,["ironcalc_base::expressions::types::ParsedRange"]],["impl RefUnwindSafe for CellReference",1,["ironcalc_base::expressions::types::CellReference"]],["impl RefUnwindSafe for CellReferenceRC",1,["ironcalc_base::expressions::types::CellReferenceRC"]],["impl RefUnwindSafe for CellReferenceIndex",1,["ironcalc_base::expressions::types::CellReferenceIndex"]],["impl RefUnwindSafe for Area",1,["ironcalc_base::expressions::types::Area"]],["impl RefUnwindSafe for Formatted",1,["ironcalc_base::formatter::format::Formatted"]],["impl RefUnwindSafe for Lexer",1,["ironcalc_base::formatter::lexer::Lexer"]],["impl RefUnwindSafe for Token",1,["ironcalc_base::formatter::lexer::Token"]],["impl RefUnwindSafe for Compare",1,["ironcalc_base::formatter::lexer::Compare"]],["impl RefUnwindSafe for Digit",1,["ironcalc_base::formatter::parser::Digit"]],["impl RefUnwindSafe for TextToken",1,["ironcalc_base::formatter::parser::TextToken"]],["impl RefUnwindSafe for NumberPart",1,["ironcalc_base::formatter::parser::NumberPart"]],["impl RefUnwindSafe for DatePart",1,["ironcalc_base::formatter::parser::DatePart"]],["impl RefUnwindSafe for ErrorPart",1,["ironcalc_base::formatter::parser::ErrorPart"]],["impl RefUnwindSafe for GeneralPart",1,["ironcalc_base::formatter::parser::GeneralPart"]],["impl RefUnwindSafe for ParsePart",1,["ironcalc_base::formatter::parser::ParsePart"]],["impl RefUnwindSafe for Parser",1,["ironcalc_base::formatter::parser::Parser"]],["impl RefUnwindSafe for Booleans",1,["ironcalc_base::language::Booleans"]],["impl RefUnwindSafe for Errors",1,["ironcalc_base::language::Errors"]],["impl RefUnwindSafe for Language",1,["ironcalc_base::language::Language"]],["impl RefUnwindSafe for Locale",1,["ironcalc_base::locale::Locale"]],["impl RefUnwindSafe for Currency",1,["ironcalc_base::locale::Currency"]],["impl RefUnwindSafe for NumbersProperties",1,["ironcalc_base::locale::NumbersProperties"]],["impl RefUnwindSafe for Dates",1,["ironcalc_base::locale::Dates"]],["impl RefUnwindSafe for NumbersSymbols",1,["ironcalc_base::locale::NumbersSymbols"]],["impl RefUnwindSafe for CurrencyFormats",1,["ironcalc_base::locale::CurrencyFormats"]],["impl RefUnwindSafe for DecimalFormats",1,["ironcalc_base::locale::DecimalFormats"]],["impl RefUnwindSafe for CellState",1,["ironcalc_base::model::CellState"]],["impl RefUnwindSafe for ParsedDefinedName",1,["ironcalc_base::model::ParsedDefinedName"]],["impl RefUnwindSafe for Model",1,["ironcalc_base::model::Model"]],["impl RefUnwindSafe for CellIndex",1,["ironcalc_base::model::CellIndex"]],["impl RefUnwindSafe for Style",1,["ironcalc_base::model::Style"]],["impl RefUnwindSafe for Metadata",1,["ironcalc_base::types::Metadata"]],["impl RefUnwindSafe for WorkbookSettings",1,["ironcalc_base::types::WorkbookSettings"]],["impl RefUnwindSafe for Workbook",1,["ironcalc_base::types::Workbook"]],["impl RefUnwindSafe for DefinedName",1,["ironcalc_base::types::DefinedName"]],["impl RefUnwindSafe for SheetState",1,["ironcalc_base::types::SheetState"]],["impl RefUnwindSafe for Worksheet",1,["ironcalc_base::types::Worksheet"]],["impl RefUnwindSafe for Row",1,["ironcalc_base::types::Row"]],["impl RefUnwindSafe for Col",1,["ironcalc_base::types::Col"]],["impl RefUnwindSafe for CellType",1,["ironcalc_base::types::CellType"]],["impl RefUnwindSafe for Cell",1,["ironcalc_base::types::Cell"]],["impl RefUnwindSafe for Comment",1,["ironcalc_base::types::Comment"]],["impl RefUnwindSafe for Table",1,["ironcalc_base::types::Table"]],["impl RefUnwindSafe for TableColumn",1,["ironcalc_base::types::TableColumn"]],["impl RefUnwindSafe for TableStyleInfo",1,["ironcalc_base::types::TableStyleInfo"]],["impl RefUnwindSafe for Styles",1,["ironcalc_base::types::Styles"]],["impl RefUnwindSafe for NumFmt",1,["ironcalc_base::types::NumFmt"]],["impl RefUnwindSafe for FontScheme",1,["ironcalc_base::types::FontScheme"]],["impl RefUnwindSafe for Font",1,["ironcalc_base::types::Font"]],["impl RefUnwindSafe for Fill",1,["ironcalc_base::types::Fill"]],["impl RefUnwindSafe for HorizontalAlignment",1,["ironcalc_base::types::HorizontalAlignment"]],["impl RefUnwindSafe for VerticalAlignment",1,["ironcalc_base::types::VerticalAlignment"]],["impl RefUnwindSafe for Alignment",1,["ironcalc_base::types::Alignment"]],["impl RefUnwindSafe for CellStyleXfs",1,["ironcalc_base::types::CellStyleXfs"]],["impl RefUnwindSafe for CellXfs",1,["ironcalc_base::types::CellXfs"]],["impl RefUnwindSafe for CellStyles",1,["ironcalc_base::types::CellStyles"]],["impl RefUnwindSafe for BorderStyle",1,["ironcalc_base::types::BorderStyle"]],["impl RefUnwindSafe for BorderItem",1,["ironcalc_base::types::BorderItem"]],["impl RefUnwindSafe for Border",1,["ironcalc_base::types::Border"]],["impl RefUnwindSafe for SheetInfo",1,["ironcalc_base::types::SheetInfo"]],["impl RefUnwindSafe for WorksheetDimension",1,["ironcalc_base::worksheet::WorksheetDimension"]],["impl RefUnwindSafe for NavigationDirection",1,["ironcalc_base::worksheet::NavigationDirection"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 0000000..ccaabe0 --- /dev/null +++ b/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js @@ -0,0 +1,4 @@ +(function() {var implementors = { +"ironcalc":[["impl UnwindSafe for CompareError",1,["ironcalc::compare::CompareError"]],["impl UnwindSafe for Diff",1,["ironcalc::compare::Diff"]],["impl UnwindSafe for XlsxError",1,["ironcalc::error::XlsxError"]]], +"ironcalc_base":[["impl UnwindSafe for CellReference",1,["ironcalc_base::calc_result::CellReference"]],["impl UnwindSafe for Range",1,["ironcalc_base::calc_result::Range"]],["impl UnwindSafe for CellValue",1,["ironcalc_base::cell::CellValue"]],["impl UnwindSafe for MarkedToken",1,["ironcalc_base::expressions::lexer::util::MarkedToken"]],["impl UnwindSafe for LexerError",1,["ironcalc_base::expressions::lexer::LexerError"]],["impl UnwindSafe for LexerMode",1,["ironcalc_base::expressions::lexer::LexerMode"]],["impl UnwindSafe for Lexer",1,["ironcalc_base::expressions::lexer::Lexer"]],["impl UnwindSafe for DisplaceData",1,["ironcalc_base::expressions::parser::stringify::DisplaceData"]],["impl UnwindSafe for Node",1,["ironcalc_base::expressions::parser::Node"]],["impl UnwindSafe for Parser",1,["ironcalc_base::expressions::parser::Parser"]],["impl UnwindSafe for OpCompare",1,["ironcalc_base::expressions::token::OpCompare"]],["impl UnwindSafe for OpUnary",1,["ironcalc_base::expressions::token::OpUnary"]],["impl UnwindSafe for OpSum",1,["ironcalc_base::expressions::token::OpSum"]],["impl UnwindSafe for OpProduct",1,["ironcalc_base::expressions::token::OpProduct"]],["impl UnwindSafe for Error",1,["ironcalc_base::expressions::token::Error"]],["impl UnwindSafe for TableSpecifier",1,["ironcalc_base::expressions::token::TableSpecifier"]],["impl UnwindSafe for TableReference",1,["ironcalc_base::expressions::token::TableReference"]],["impl UnwindSafe for TokenType",1,["ironcalc_base::expressions::token::TokenType"]],["impl UnwindSafe for ParsedReference",1,["ironcalc_base::expressions::types::ParsedReference"]],["impl UnwindSafe for ParsedRange",1,["ironcalc_base::expressions::types::ParsedRange"]],["impl UnwindSafe for CellReference",1,["ironcalc_base::expressions::types::CellReference"]],["impl UnwindSafe for CellReferenceRC",1,["ironcalc_base::expressions::types::CellReferenceRC"]],["impl UnwindSafe for CellReferenceIndex",1,["ironcalc_base::expressions::types::CellReferenceIndex"]],["impl UnwindSafe for Area",1,["ironcalc_base::expressions::types::Area"]],["impl UnwindSafe for Formatted",1,["ironcalc_base::formatter::format::Formatted"]],["impl UnwindSafe for Lexer",1,["ironcalc_base::formatter::lexer::Lexer"]],["impl UnwindSafe for Token",1,["ironcalc_base::formatter::lexer::Token"]],["impl UnwindSafe for Compare",1,["ironcalc_base::formatter::lexer::Compare"]],["impl UnwindSafe for Digit",1,["ironcalc_base::formatter::parser::Digit"]],["impl UnwindSafe for TextToken",1,["ironcalc_base::formatter::parser::TextToken"]],["impl UnwindSafe for NumberPart",1,["ironcalc_base::formatter::parser::NumberPart"]],["impl UnwindSafe for DatePart",1,["ironcalc_base::formatter::parser::DatePart"]],["impl UnwindSafe for ErrorPart",1,["ironcalc_base::formatter::parser::ErrorPart"]],["impl UnwindSafe for GeneralPart",1,["ironcalc_base::formatter::parser::GeneralPart"]],["impl UnwindSafe for ParsePart",1,["ironcalc_base::formatter::parser::ParsePart"]],["impl UnwindSafe for Parser",1,["ironcalc_base::formatter::parser::Parser"]],["impl UnwindSafe for Booleans",1,["ironcalc_base::language::Booleans"]],["impl UnwindSafe for Errors",1,["ironcalc_base::language::Errors"]],["impl UnwindSafe for Language",1,["ironcalc_base::language::Language"]],["impl UnwindSafe for Locale",1,["ironcalc_base::locale::Locale"]],["impl UnwindSafe for Currency",1,["ironcalc_base::locale::Currency"]],["impl UnwindSafe for NumbersProperties",1,["ironcalc_base::locale::NumbersProperties"]],["impl UnwindSafe for Dates",1,["ironcalc_base::locale::Dates"]],["impl UnwindSafe for NumbersSymbols",1,["ironcalc_base::locale::NumbersSymbols"]],["impl UnwindSafe for CurrencyFormats",1,["ironcalc_base::locale::CurrencyFormats"]],["impl UnwindSafe for DecimalFormats",1,["ironcalc_base::locale::DecimalFormats"]],["impl UnwindSafe for CellState",1,["ironcalc_base::model::CellState"]],["impl UnwindSafe for ParsedDefinedName",1,["ironcalc_base::model::ParsedDefinedName"]],["impl UnwindSafe for Model",1,["ironcalc_base::model::Model"]],["impl UnwindSafe for CellIndex",1,["ironcalc_base::model::CellIndex"]],["impl UnwindSafe for Style",1,["ironcalc_base::model::Style"]],["impl UnwindSafe for Metadata",1,["ironcalc_base::types::Metadata"]],["impl UnwindSafe for WorkbookSettings",1,["ironcalc_base::types::WorkbookSettings"]],["impl UnwindSafe for Workbook",1,["ironcalc_base::types::Workbook"]],["impl UnwindSafe for DefinedName",1,["ironcalc_base::types::DefinedName"]],["impl UnwindSafe for SheetState",1,["ironcalc_base::types::SheetState"]],["impl UnwindSafe for Worksheet",1,["ironcalc_base::types::Worksheet"]],["impl UnwindSafe for Row",1,["ironcalc_base::types::Row"]],["impl UnwindSafe for Col",1,["ironcalc_base::types::Col"]],["impl UnwindSafe for CellType",1,["ironcalc_base::types::CellType"]],["impl UnwindSafe for Cell",1,["ironcalc_base::types::Cell"]],["impl UnwindSafe for Comment",1,["ironcalc_base::types::Comment"]],["impl UnwindSafe for Table",1,["ironcalc_base::types::Table"]],["impl UnwindSafe for TableColumn",1,["ironcalc_base::types::TableColumn"]],["impl UnwindSafe for TableStyleInfo",1,["ironcalc_base::types::TableStyleInfo"]],["impl UnwindSafe for Styles",1,["ironcalc_base::types::Styles"]],["impl UnwindSafe for NumFmt",1,["ironcalc_base::types::NumFmt"]],["impl UnwindSafe for FontScheme",1,["ironcalc_base::types::FontScheme"]],["impl UnwindSafe for Font",1,["ironcalc_base::types::Font"]],["impl UnwindSafe for Fill",1,["ironcalc_base::types::Fill"]],["impl UnwindSafe for HorizontalAlignment",1,["ironcalc_base::types::HorizontalAlignment"]],["impl UnwindSafe for VerticalAlignment",1,["ironcalc_base::types::VerticalAlignment"]],["impl UnwindSafe for Alignment",1,["ironcalc_base::types::Alignment"]],["impl UnwindSafe for CellStyleXfs",1,["ironcalc_base::types::CellStyleXfs"]],["impl UnwindSafe for CellXfs",1,["ironcalc_base::types::CellXfs"]],["impl UnwindSafe for CellStyles",1,["ironcalc_base::types::CellStyles"]],["impl UnwindSafe for BorderStyle",1,["ironcalc_base::types::BorderStyle"]],["impl UnwindSafe for BorderItem",1,["ironcalc_base::types::BorderItem"]],["impl UnwindSafe for Border",1,["ironcalc_base::types::Border"]],["impl UnwindSafe for SheetInfo",1,["ironcalc_base::types::SheetInfo"]],["impl UnwindSafe for WorksheetDimension",1,["ironcalc_base::worksheet::WorksheetDimension"]],["impl UnwindSafe for NavigationDirection",1,["ironcalc_base::worksheet::NavigationDirection"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/serde/de/trait.Deserialize.js b/trait.impl/serde/de/trait.Deserialize.js new file mode 100644 index 0000000..a501468 --- /dev/null +++ b/trait.impl/serde/de/trait.Deserialize.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"ironcalc_base":[["impl<'de> Deserialize<'de> for TableColumn"],["impl<'de> Deserialize<'de> for CellValue"],["impl<'de> Deserialize<'de> for Worksheet"],["impl<'de> Deserialize<'de> for SheetState"],["impl<'de> Deserialize<'de> for ParsedReference"],["impl<'de> Deserialize<'de> for Metadata"],["impl<'de> Deserialize<'de> for SheetInfo"],["impl<'de> Deserialize<'de> for Locale"],["impl<'de> Deserialize<'de> for Workbook"],["impl<'de> Deserialize<'de> for CellStyles"],["impl<'de> Deserialize<'de> for CurrencyFormats"],["impl<'de> Deserialize<'de> for NumbersProperties"],["impl<'de> Deserialize<'de> for Currency"],["impl<'de> Deserialize<'de> for CellStyleXfs"],["impl<'de> Deserialize<'de> for Cell"],["impl<'de> Deserialize<'de> for VerticalAlignment"],["impl<'de> Deserialize<'de> for Dates"],["impl<'de> Deserialize<'de> for NumbersSymbols"],["impl<'de> Deserialize<'de> for CellReferenceIndex"],["impl<'de> Deserialize<'de> for TableStyleInfo"],["impl<'de> Deserialize<'de> for Style"],["impl<'de> Deserialize<'de> for NumFmt"],["impl<'de> Deserialize<'de> for Errors"],["impl<'de> Deserialize<'de> for FontScheme"],["impl<'de> Deserialize<'de> for Language"],["impl<'de> Deserialize<'de> for BorderItem"],["impl<'de> Deserialize<'de> for BorderStyle"],["impl<'de> Deserialize<'de> for HorizontalAlignment"],["impl<'de> Deserialize<'de> for Styles"],["impl<'de> Deserialize<'de> for DecimalFormats"],["impl<'de> Deserialize<'de> for Table"],["impl<'de> Deserialize<'de> for Error"],["impl<'de> Deserialize<'de> for CellXfs"],["impl<'de> Deserialize<'de> for Booleans"],["impl<'de> Deserialize<'de> for Fill"],["impl<'de> Deserialize<'de> for Font"],["impl<'de> Deserialize<'de> for Row"],["impl<'de> Deserialize<'de> for Area"],["impl<'de> Deserialize<'de> for WorkbookSettings"],["impl<'de> Deserialize<'de> for Col"],["impl<'de> Deserialize<'de> for Comment"],["impl<'de> Deserialize<'de> for DefinedName"],["impl<'de> Deserialize<'de> for Alignment"],["impl<'de> Deserialize<'de> for Border"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/serde/ser/trait.Serialize.js b/trait.impl/serde/ser/trait.Serialize.js new file mode 100644 index 0000000..d666aa6 --- /dev/null +++ b/trait.impl/serde/ser/trait.Serialize.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"ironcalc_base":[["impl Serialize for Alignment"],["impl Serialize for Workbook"],["impl Serialize for Dates"],["impl Serialize for CellReferenceIndex"],["impl Serialize for DecimalFormats"],["impl Serialize for Errors"],["impl Serialize for Row"],["impl Serialize for FontScheme"],["impl Serialize for VerticalAlignment"],["impl Serialize for CellValue"],["impl Serialize for BorderItem"],["impl Serialize for Area"],["impl Serialize for Font"],["impl Serialize for NumbersSymbols"],["impl Serialize for DefinedName"],["impl Serialize for SheetInfo"],["impl Serialize for SheetState"],["impl Serialize for Styles"],["impl Serialize for Col"],["impl Serialize for Language"],["impl Serialize for CellStyleXfs"],["impl Serialize for NumbersProperties"],["impl Serialize for ParsedReference"],["impl Serialize for Table"],["impl Serialize for Fill"],["impl Serialize for Error"],["impl Serialize for Worksheet"],["impl Serialize for TableStyleInfo"],["impl Serialize for HorizontalAlignment"],["impl Serialize for Booleans"],["impl Serialize for CellStyles"],["impl Serialize for NumFmt"],["impl Serialize for Cell"],["impl Serialize for Border"],["impl Serialize for TableColumn"],["impl Serialize for Style"],["impl Serialize for Metadata"],["impl Serialize for Comment"],["impl Serialize for Currency"],["impl Serialize for CurrencyFormats"],["impl Serialize for BorderStyle"],["impl Serialize for WorkbookSettings"],["impl Serialize for CellXfs"],["impl Serialize for Locale"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/type.impl/std/collections/hash/map/struct.HashMap.js b/type.impl/std/collections/hash/map/struct.HashMap.js new file mode 100644 index 0000000..362e825 --- /dev/null +++ b/type.impl/std/collections/hash/map/struct.HashMap.js @@ -0,0 +1,3 @@ +(function() {var type_impls = { +"ironcalc_base":[["
source§

impl<K, V> HashMap<K, V>

1.0.0 · source

pub fn new() -> HashMap<K, V>

Creates an empty HashMap.

\n

The hash map is initially created with a capacity of 0, so it will not allocate until it\nis first inserted into.

\n
Examples
\n
use std::collections::HashMap;\nlet mut map: HashMap<&str, i32> = HashMap::new();
\n
1.0.0 · source

pub fn with_capacity(capacity: usize) -> HashMap<K, V>

Creates an empty HashMap with at least the specified capacity.

\n

The hash map will be able to hold at least capacity elements without\nreallocating. This method is allowed to allocate for more elements than\ncapacity. If capacity is 0, the hash map will not allocate.

\n
Examples
\n
use std::collections::HashMap;\nlet mut map: HashMap<&str, i32> = HashMap::with_capacity(10);
\n
",0,"ironcalc_base::types::SheetData"],["
source§

impl<K, V, S> HashMap<K, V, S>

1.7.0 (const: unstable) · source

pub fn with_hasher(hash_builder: S) -> HashMap<K, V, S>

Creates an empty HashMap which will use the given hash builder to hash\nkeys.

\n

The created map has the default initial capacity.

\n

Warning: hash_builder is normally randomly generated, and\nis designed to allow HashMaps to be resistant to attacks that\ncause many collisions and very poor performance. Setting it\nmanually using this function can expose a DoS attack vector.

\n

The hash_builder passed should implement the BuildHasher trait for\nthe HashMap to be useful, see its documentation for details.

\n
Examples
\n
use std::collections::HashMap;\nuse std::collections::hash_map::RandomState;\n\nlet s = RandomState::new();\nlet mut map = HashMap::with_hasher(s);\nmap.insert(1, 2);
\n
1.7.0 · source

pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashMap<K, V, S>

Creates an empty HashMap with at least the specified capacity, using\nhasher to hash the keys.

\n

The hash map will be able to hold at least capacity elements without\nreallocating. This method is allowed to allocate for more elements than\ncapacity. If capacity is 0, the hash map will not allocate.

\n

Warning: hasher is normally randomly generated, and\nis designed to allow HashMaps to be resistant to attacks that\ncause many collisions and very poor performance. Setting it\nmanually using this function can expose a DoS attack vector.

\n

The hasher passed should implement the BuildHasher trait for\nthe HashMap to be useful, see its documentation for details.

\n
Examples
\n
use std::collections::HashMap;\nuse std::collections::hash_map::RandomState;\n\nlet s = RandomState::new();\nlet mut map = HashMap::with_capacity_and_hasher(10, s);\nmap.insert(1, 2);
\n
1.0.0 · source

pub fn capacity(&self) -> usize

Returns the number of elements the map can hold without reallocating.

\n

This number is a lower bound; the HashMap<K, V> might be able to hold\nmore, but is guaranteed to be able to hold at least this many.

\n
Examples
\n
use std::collections::HashMap;\nlet map: HashMap<i32, i32> = HashMap::with_capacity(100);\nassert!(map.capacity() >= 100);
\n
1.0.0 · source

pub fn keys(&self) -> Keys<'_, K, V>

An iterator visiting all keys in arbitrary order.\nThe iterator element type is &'a K.

\n
Examples
\n
use std::collections::HashMap;\n\nlet map = HashMap::from([\n    ("a", 1),\n    ("b", 2),\n    ("c", 3),\n]);\n\nfor key in map.keys() {\n    println!("{key}");\n}
\n
Performance
\n

In the current implementation, iterating over keys takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.54.0 · source

pub fn into_keys(self) -> IntoKeys<K, V>

Creates a consuming iterator visiting all the keys in arbitrary order.\nThe map cannot be used after calling this.\nThe iterator element type is K.

\n
Examples
\n
use std::collections::HashMap;\n\nlet map = HashMap::from([\n    ("a", 1),\n    ("b", 2),\n    ("c", 3),\n]);\n\nlet mut vec: Vec<&str> = map.into_keys().collect();\n// The `IntoKeys` iterator produces keys in arbitrary order, so the\n// keys must be sorted to test them against a sorted array.\nvec.sort_unstable();\nassert_eq!(vec, ["a", "b", "c"]);
\n
Performance
\n

In the current implementation, iterating over keys takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.0.0 · source

pub fn values(&self) -> Values<'_, K, V>

An iterator visiting all values in arbitrary order.\nThe iterator element type is &'a V.

\n
Examples
\n
use std::collections::HashMap;\n\nlet map = HashMap::from([\n    ("a", 1),\n    ("b", 2),\n    ("c", 3),\n]);\n\nfor val in map.values() {\n    println!("{val}");\n}
\n
Performance
\n

In the current implementation, iterating over values takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.10.0 · source

pub fn values_mut(&mut self) -> ValuesMut<'_, K, V>

An iterator visiting all values mutably in arbitrary order.\nThe iterator element type is &'a mut V.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::from([\n    ("a", 1),\n    ("b", 2),\n    ("c", 3),\n]);\n\nfor val in map.values_mut() {\n    *val = *val + 10;\n}\n\nfor val in map.values() {\n    println!("{val}");\n}
\n
Performance
\n

In the current implementation, iterating over values takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.54.0 · source

pub fn into_values(self) -> IntoValues<K, V>

Creates a consuming iterator visiting all the values in arbitrary order.\nThe map cannot be used after calling this.\nThe iterator element type is V.

\n
Examples
\n
use std::collections::HashMap;\n\nlet map = HashMap::from([\n    ("a", 1),\n    ("b", 2),\n    ("c", 3),\n]);\n\nlet mut vec: Vec<i32> = map.into_values().collect();\n// The `IntoValues` iterator produces values in arbitrary order, so\n// the values must be sorted to test them against a sorted array.\nvec.sort_unstable();\nassert_eq!(vec, [1, 2, 3]);
\n
Performance
\n

In the current implementation, iterating over values takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.0.0 · source

pub fn iter(&self) -> Iter<'_, K, V>

An iterator visiting all key-value pairs in arbitrary order.\nThe iterator element type is (&'a K, &'a V).

\n
Examples
\n
use std::collections::HashMap;\n\nlet map = HashMap::from([\n    ("a", 1),\n    ("b", 2),\n    ("c", 3),\n]);\n\nfor (key, val) in map.iter() {\n    println!("key: {key} val: {val}");\n}
\n
Performance
\n

In the current implementation, iterating over map takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.0.0 · source

pub fn iter_mut(&mut self) -> IterMut<'_, K, V>

An iterator visiting all key-value pairs in arbitrary order,\nwith mutable references to the values.\nThe iterator element type is (&'a K, &'a mut V).

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::from([\n    ("a", 1),\n    ("b", 2),\n    ("c", 3),\n]);\n\n// Update all values\nfor (_, val) in map.iter_mut() {\n    *val *= 2;\n}\n\nfor (key, val) in &map {\n    println!("key: {key} val: {val}");\n}
\n
Performance
\n

In the current implementation, iterating over map takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.0.0 · source

pub fn len(&self) -> usize

Returns the number of elements in the map.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut a = HashMap::new();\nassert_eq!(a.len(), 0);\na.insert(1, "a");\nassert_eq!(a.len(), 1);
\n
1.0.0 · source

pub fn is_empty(&self) -> bool

Returns true if the map contains no elements.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut a = HashMap::new();\nassert!(a.is_empty());\na.insert(1, "a");\nassert!(!a.is_empty());
\n
1.6.0 · source

pub fn drain(&mut self) -> Drain<'_, K, V>

Clears the map, returning all key-value pairs as an iterator. Keeps the\nallocated memory for reuse.

\n

If the returned iterator is dropped before being fully consumed, it\ndrops the remaining key-value pairs. The returned iterator keeps a\nmutable borrow on the map to optimize its implementation.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut a = HashMap::new();\na.insert(1, "a");\na.insert(2, "b");\n\nfor (k, v) in a.drain().take(1) {\n    assert!(k == 1 || k == 2);\n    assert!(v == "a" || v == "b");\n}\n\nassert!(a.is_empty());
\n
source

pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, K, V, F>where\n F: FnMut(&K, &mut V) -> bool,

🔬This is a nightly-only experimental API. (hash_extract_if)

Creates an iterator which uses a closure to determine if an element should be removed.

\n

If the closure returns true, the element is removed from the map and yielded.\nIf the closure returns false, or panics, the element remains in the map and will not be\nyielded.

\n

Note that extract_if lets you mutate every value in the filter closure, regardless of\nwhether you choose to keep or remove it.

\n

If the returned ExtractIf is not exhausted, e.g. because it is dropped without iterating\nor the iteration short-circuits, then the remaining elements will be retained.\nUse retain with a negated predicate if you do not need the returned iterator.

\n
Examples
\n

Splitting a map into even and odd keys, reusing the original map:

\n\n
#![feature(hash_extract_if)]\nuse std::collections::HashMap;\n\nlet mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x)).collect();\nlet extracted: HashMap<i32, i32> = map.extract_if(|k, _v| k % 2 == 0).collect();\n\nlet mut evens = extracted.keys().copied().collect::<Vec<_>>();\nlet mut odds = map.keys().copied().collect::<Vec<_>>();\nevens.sort();\nodds.sort();\n\nassert_eq!(evens, vec![0, 2, 4, 6]);\nassert_eq!(odds, vec![1, 3, 5, 7]);
\n
1.18.0 · source

pub fn retain<F>(&mut self, f: F)where\n F: FnMut(&K, &mut V) -> bool,

Retains only the elements specified by the predicate.

\n

In other words, remove all pairs (k, v) for which f(&k, &mut v) returns false.\nThe elements are visited in unsorted (and unspecified) order.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x*10)).collect();\nmap.retain(|&k, _| k % 2 == 0);\nassert_eq!(map.len(), 4);
\n
Performance
\n

In the current implementation, this operation takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.

\n
1.0.0 · source

pub fn clear(&mut self)

Clears the map, removing all key-value pairs. Keeps the allocated memory\nfor reuse.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut a = HashMap::new();\na.insert(1, "a");\na.clear();\nassert!(a.is_empty());
\n
1.9.0 · source

pub fn hasher(&self) -> &S

Returns a reference to the map’s BuildHasher.

\n
Examples
\n
use std::collections::HashMap;\nuse std::collections::hash_map::RandomState;\n\nlet hasher = RandomState::new();\nlet map: HashMap<i32, i32> = HashMap::with_hasher(hasher);\nlet hasher: &RandomState = map.hasher();
\n
",0,"ironcalc_base::types::SheetData"],["
source§

impl<K, V, S> HashMap<K, V, S>where\n K: Eq + Hash,\n S: BuildHasher,

1.0.0 · source

pub fn reserve(&mut self, additional: usize)

Reserves capacity for at least additional more elements to be inserted\nin the HashMap. The collection may reserve more space to speculatively\navoid frequent reallocations. After calling reserve,\ncapacity will be greater than or equal to self.len() + additional.\nDoes nothing if capacity is already sufficient.

\n
Panics
\n

Panics if the new allocation size overflows usize.

\n
Examples
\n
use std::collections::HashMap;\nlet mut map: HashMap<&str, i32> = HashMap::new();\nmap.reserve(10);
\n
1.57.0 · source

pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>

Tries to reserve capacity for at least additional more elements to be inserted\nin the HashMap. The collection may reserve more space to speculatively\navoid frequent reallocations. After calling try_reserve,\ncapacity will be greater than or equal to self.len() + additional if\nit returns Ok(()).\nDoes nothing if capacity is already sufficient.

\n
Errors
\n

If the capacity overflows, or the allocator reports a failure, then an error\nis returned.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map: HashMap<&str, isize> = HashMap::new();\nmap.try_reserve(10).expect("why is the test harness OOMing on a handful of bytes?");
\n
1.0.0 · source

pub fn shrink_to_fit(&mut self)

Shrinks the capacity of the map as much as possible. It will drop\ndown as much as possible while maintaining the internal rules\nand possibly leaving some space in accordance with the resize policy.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map: HashMap<i32, i32> = HashMap::with_capacity(100);\nmap.insert(1, 2);\nmap.insert(3, 4);\nassert!(map.capacity() >= 100);\nmap.shrink_to_fit();\nassert!(map.capacity() >= 2);
\n
1.56.0 · source

pub fn shrink_to(&mut self, min_capacity: usize)

Shrinks the capacity of the map with a lower limit. It will drop\ndown no lower than the supplied limit while maintaining the internal rules\nand possibly leaving some space in accordance with the resize policy.

\n

If the current capacity is less than the lower limit, this is a no-op.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map: HashMap<i32, i32> = HashMap::with_capacity(100);\nmap.insert(1, 2);\nmap.insert(3, 4);\nassert!(map.capacity() >= 100);\nmap.shrink_to(10);\nassert!(map.capacity() >= 10);\nmap.shrink_to(0);\nassert!(map.capacity() >= 2);
\n
1.0.0 · source

pub fn entry(&mut self, key: K) -> Entry<'_, K, V>

Gets the given key’s corresponding entry in the map for in-place manipulation.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut letters = HashMap::new();\n\nfor ch in "a short treatise on fungi".chars() {\n    letters.entry(ch).and_modify(|counter| *counter += 1).or_insert(1);\n}\n\nassert_eq!(letters[&'s'], 2);\nassert_eq!(letters[&'t'], 3);\nassert_eq!(letters[&'u'], 1);\nassert_eq!(letters.get(&'y'), None);
\n
1.0.0 · source

pub fn get<Q>(&self, k: &Q) -> Option<&V>where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

Returns a reference to the value corresponding to the key.

\n

The key may be any borrowed form of the map’s key type, but\nHash and Eq on the borrowed form must match those for\nthe key type.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, "a");\nassert_eq!(map.get(&1), Some(&"a"));\nassert_eq!(map.get(&2), None);
\n
1.40.0 · source

pub fn get_key_value<Q>(&self, k: &Q) -> Option<(&K, &V)>where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

Returns the key-value pair corresponding to the supplied key.

\n

The supplied key may be any borrowed form of the map’s key type, but\nHash and Eq on the borrowed form must match those for\nthe key type.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, "a");\nassert_eq!(map.get_key_value(&1), Some((&1, &"a")));\nassert_eq!(map.get_key_value(&2), None);
\n
source

pub fn get_many_mut<Q, const N: usize>(\n &mut self,\n ks: [&Q; N]\n) -> Option<[&mut V; N]>where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

🔬This is a nightly-only experimental API. (map_many_mut)

Attempts to get mutable references to N values in the map at once.

\n

Returns an array of length N with the results of each query. For soundness, at most one\nmutable reference will be returned to any value. None will be returned if any of the\nkeys are duplicates or missing.

\n
Examples
\n
#![feature(map_many_mut)]\nuse std::collections::HashMap;\n\nlet mut libraries = HashMap::new();\nlibraries.insert("Bodleian Library".to_string(), 1602);\nlibraries.insert("Athenæum".to_string(), 1807);\nlibraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);\nlibraries.insert("Library of Congress".to_string(), 1800);\n\nlet got = libraries.get_many_mut([\n    "Athenæum",\n    "Library of Congress",\n]);\nassert_eq!(\n    got,\n    Some([\n        &mut 1807,\n        &mut 1800,\n    ]),\n);\n\n// Missing keys result in None\nlet got = libraries.get_many_mut([\n    "Athenæum",\n    "New York Public Library",\n]);\nassert_eq!(got, None);\n\n// Duplicate keys result in None\nlet got = libraries.get_many_mut([\n    "Athenæum",\n    "Athenæum",\n]);\nassert_eq!(got, None);
\n
source

pub unsafe fn get_many_unchecked_mut<Q, const N: usize>(\n &mut self,\n ks: [&Q; N]\n) -> Option<[&mut V; N]>where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

🔬This is a nightly-only experimental API. (map_many_mut)

Attempts to get mutable references to N values in the map at once, without validating that\nthe values are unique.

\n

Returns an array of length N with the results of each query. None will be returned if\nany of the keys are missing.

\n

For a safe alternative see get_many_mut.

\n
Safety
\n

Calling this method with overlapping keys is undefined behavior even if the resulting\nreferences are not used.

\n
Examples
\n
#![feature(map_many_mut)]\nuse std::collections::HashMap;\n\nlet mut libraries = HashMap::new();\nlibraries.insert("Bodleian Library".to_string(), 1602);\nlibraries.insert("Athenæum".to_string(), 1807);\nlibraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);\nlibraries.insert("Library of Congress".to_string(), 1800);\n\nlet got = libraries.get_many_mut([\n    "Athenæum",\n    "Library of Congress",\n]);\nassert_eq!(\n    got,\n    Some([\n        &mut 1807,\n        &mut 1800,\n    ]),\n);\n\n// Missing keys result in None\nlet got = libraries.get_many_mut([\n    "Athenæum",\n    "New York Public Library",\n]);\nassert_eq!(got, None);
\n
1.0.0 · source

pub fn contains_key<Q>(&self, k: &Q) -> boolwhere\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

Returns true if the map contains a value for the specified key.

\n

The key may be any borrowed form of the map’s key type, but\nHash and Eq on the borrowed form must match those for\nthe key type.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, "a");\nassert_eq!(map.contains_key(&1), true);\nassert_eq!(map.contains_key(&2), false);
\n
1.0.0 · source

pub fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V>where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

Returns a mutable reference to the value corresponding to the key.

\n

The key may be any borrowed form of the map’s key type, but\nHash and Eq on the borrowed form must match those for\nthe key type.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, "a");\nif let Some(x) = map.get_mut(&1) {\n    *x = "b";\n}\nassert_eq!(map[&1], "b");
\n
1.0.0 · source

pub fn insert(&mut self, k: K, v: V) -> Option<V>

Inserts a key-value pair into the map.

\n

If the map did not have this key present, None is returned.

\n

If the map did have this key present, the value is updated, and the old\nvalue is returned. The key is not updated, though; this matters for\ntypes that can be == without being identical. See the module-level\ndocumentation for more.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nassert_eq!(map.insert(37, "a"), None);\nassert_eq!(map.is_empty(), false);\n\nmap.insert(37, "b");\nassert_eq!(map.insert(37, "c"), Some("b"));\nassert_eq!(map[&37], "c");
\n
source

pub fn try_insert(\n &mut self,\n key: K,\n value: V\n) -> Result<&mut V, OccupiedError<'_, K, V>>

🔬This is a nightly-only experimental API. (map_try_insert)

Tries to insert a key-value pair into the map, and returns\na mutable reference to the value in the entry.

\n

If the map already had this key present, nothing is updated, and\nan error containing the occupied entry and the value is returned.

\n
Examples
\n

Basic usage:

\n\n
#![feature(map_try_insert)]\n\nuse std::collections::HashMap;\n\nlet mut map = HashMap::new();\nassert_eq!(map.try_insert(37, "a").unwrap(), &"a");\n\nlet err = map.try_insert(37, "b").unwrap_err();\nassert_eq!(err.entry.key(), &37);\nassert_eq!(err.entry.get(), &"a");\nassert_eq!(err.value, "b");
\n
1.0.0 · source

pub fn remove<Q>(&mut self, k: &Q) -> Option<V>where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

Removes a key from the map, returning the value at the key if the key\nwas previously in the map.

\n

The key may be any borrowed form of the map’s key type, but\nHash and Eq on the borrowed form must match those for\nthe key type.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, "a");\nassert_eq!(map.remove(&1), Some("a"));\nassert_eq!(map.remove(&1), None);
\n
1.27.0 · source

pub fn remove_entry<Q>(&mut self, k: &Q) -> Option<(K, V)>where\n K: Borrow<Q>,\n Q: Hash + Eq + ?Sized,

Removes a key from the map, returning the stored key and value if the\nkey was previously in the map.

\n

The key may be any borrowed form of the map’s key type, but\nHash and Eq on the borrowed form must match those for\nthe key type.

\n
Examples
\n
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, "a");\nassert_eq!(map.remove_entry(&1), Some((1, "a")));\nassert_eq!(map.remove(&1), None);
\n
",0,"ironcalc_base::types::SheetData"],["
source§

impl<K, V, S> HashMap<K, V, S>where\n S: BuildHasher,

source

pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<'_, K, V, S>

🔬This is a nightly-only experimental API. (hash_raw_entry)

Creates a raw entry builder for the HashMap.

\n

Raw entries provide the lowest level of control for searching and\nmanipulating a map. They must be manually initialized with a hash and\nthen manually searched. After this, insertions into a vacant entry\nstill require an owned key to be provided.

\n

Raw entries are useful for such exotic situations as:

\n
    \n
  • Hash memoization
  • \n
  • Deferring the creation of an owned key until it is known to be required
  • \n
  • Using a search key that doesn’t work with the Borrow trait
  • \n
  • Using custom comparison logic without newtype wrappers
  • \n
\n

Because raw entries provide much more low-level control, it’s much easier\nto put the HashMap into an inconsistent state which, while memory-safe,\nwill cause the map to produce seemingly random results. Higher-level and\nmore foolproof APIs like entry should be preferred when possible.

\n

In particular, the hash used to initialized the raw entry must still be\nconsistent with the hash of the key that is ultimately stored in the entry.\nThis is because implementations of HashMap may need to recompute hashes\nwhen resizing, at which point only the keys are available.

\n

Raw entries give mutable access to the keys. This must not be used\nto modify how the key would compare or hash, as the map will not re-evaluate\nwhere the key should go, meaning the keys may become “lost” if their\nlocation does not reflect their state. For instance, if you change a key\nso that the map now contains keys which compare equal, search may start\nacting erratically, with two keys randomly masking each other. Implementations\nare free to assume this doesn’t happen (within the limits of memory-safety).

\n
source

pub fn raw_entry(&self) -> RawEntryBuilder<'_, K, V, S>

🔬This is a nightly-only experimental API. (hash_raw_entry)

Creates a raw immutable entry builder for the HashMap.

\n

Raw entries provide the lowest level of control for searching and\nmanipulating a map. They must be manually initialized with a hash and\nthen manually searched.

\n

This is useful for

\n
    \n
  • Hash memoization
  • \n
  • Using a search key that doesn’t work with the Borrow trait
  • \n
  • Using custom comparison logic without newtype wrappers
  • \n
\n

Unless you are in such a situation, higher-level and more foolproof APIs like\nget should be preferred.

\n

Immutable raw entries have very limited use; you might instead want raw_entry_mut.

\n
",0,"ironcalc_base::types::SheetData"],["
1.0.0 · source§

impl<K, Q, V, S> Index<&Q> for HashMap<K, V, S>where\n K: Eq + Hash + Borrow<Q>,\n Q: Eq + Hash + ?Sized,\n S: BuildHasher,

source§

fn index(&self, key: &Q) -> &V

Returns a reference to the value corresponding to the supplied key.

\n
Panics
\n

Panics if the key is not present in the HashMap.

\n
§

type Output = V

The returned type after indexing.
","Index<&Q>","ironcalc_base::types::SheetData"],["
1.0.0 · source§

impl<K, V, S> Extend<(K, V)> for HashMap<K, V, S>where\n K: Eq + Hash,\n S: BuildHasher,

Inserts all new key-values from the iterator and replaces values with existing\nkeys with new values returned from the iterator.

\n
source§

fn extend<T>(&mut self, iter: T)where\n T: IntoIterator<Item = (K, V)>,

Extends a collection with the contents of an iterator. Read more
source§

fn extend_one(&mut self, _: (K, V))

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
","Extend<(K, V)>","ironcalc_base::types::SheetData"],["
1.4.0 · source§

impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap<K, V, S>where\n K: Eq + Hash + Copy,\n V: Copy,\n S: BuildHasher,

source§

fn extend<T>(&mut self, iter: T)where\n T: IntoIterator<Item = (&'a K, &'a V)>,

Extends a collection with the contents of an iterator. Read more
source§

fn extend_one(&mut self, _: (&'a K, &'a V))

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
","Extend<(&'a K, &'a V)>","ironcalc_base::types::SheetData"],["
1.0.0 · source§

impl<K, V, S> PartialEq for HashMap<K, V, S>where\n K: Eq + Hash,\n V: PartialEq,\n S: BuildHasher,

source§

fn eq(&self, other: &HashMap<K, V, S>) -> bool

This method tests for self and other values to be equal, and is used\nby ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always\nsufficient, and should not be overridden without very good reason.
","PartialEq","ironcalc_base::types::SheetData"],["
1.0.0 · source§

impl<K, V, S> Default for HashMap<K, V, S>where\n S: Default,

source§

fn default() -> HashMap<K, V, S>

Creates an empty HashMap<K, V, S>, with the Default value for the hasher.

\n
","Default","ironcalc_base::types::SheetData"],["
1.0.0 · source§

impl<K, V, S> IntoIterator for HashMap<K, V, S>

source§

fn into_iter(self) -> IntoIter<K, V>

Creates a consuming iterator, that is, one that moves each key-value\npair out of the map in arbitrary order. The map cannot be used after\ncalling this.

\n
Examples
\n
use std::collections::HashMap;\n\nlet map = HashMap::from([\n    ("a", 1),\n    ("b", 2),\n    ("c", 3),\n]);\n\n// Not possible with .iter()\nlet vec: Vec<(&str, i32)> = map.into_iter().collect();
\n
§

type Item = (K, V)

The type of the elements being iterated over.
§

type IntoIter = IntoIter<K, V>

Which kind of iterator are we turning this into?
","IntoIterator","ironcalc_base::types::SheetData"],["
1.56.0 · source§

impl<K, V, const N: usize> From<[(K, V); N]> for HashMap<K, V>where\n K: Eq + Hash,

source§

fn from(arr: [(K, V); N]) -> HashMap<K, V>

Examples
\n
use std::collections::HashMap;\n\nlet map1 = HashMap::from([(1, 2), (3, 4)]);\nlet map2: HashMap<_, _> = [(1, 2), (3, 4)].into();\nassert_eq!(map1, map2);
\n
","From<[(K, V); N]>","ironcalc_base::types::SheetData"],["
1.36.0 · source§

impl<K, V, S> UnwindSafe for HashMap<K, V, S>where\n K: UnwindSafe,\n V: UnwindSafe,\n S: UnwindSafe,

","UnwindSafe","ironcalc_base::types::SheetData"],["
1.0.0 · source§

impl<K, V, S> Eq for HashMap<K, V, S>where\n K: Eq + Hash,\n V: Eq,\n S: BuildHasher,

","Eq","ironcalc_base::types::SheetData"],["
1.0.0 · source§

impl<K, V, S> Debug for HashMap<K, V, S>where\n K: Debug,\n V: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
","Debug","ironcalc_base::types::SheetData"],["
1.0.0 · source§

impl<K, V, S> Clone for HashMap<K, V, S>where\n K: Clone,\n V: Clone,\n S: Clone,

source§

fn clone(&self) -> HashMap<K, V, S>

Returns a copy of the value. Read more
source§

fn clone_from(&mut self, other: &HashMap<K, V, S>)

Performs copy-assignment from source. Read more
","Clone","ironcalc_base::types::SheetData"],["
1.0.0 · source§

impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S>where\n K: Eq + Hash,\n S: BuildHasher + Default,

source§

fn from_iter<T>(iter: T) -> HashMap<K, V, S>where\n T: IntoIterator<Item = (K, V)>,

Creates a value from an iterator. Read more
","FromIterator<(K, V)>","ironcalc_base::types::SheetData"],["
source§

impl<'de, K, V, S, E> IntoDeserializer<'de, E> for HashMap<K, V, S>where\n K: IntoDeserializer<'de, E> + Eq + Hash,\n V: IntoDeserializer<'de, E>,\n S: BuildHasher,\n E: Error,

§

type Deserializer = MapDeserializer<'de, <HashMap<K, V, S> as IntoIterator>::IntoIter, E>

The type of the deserializer being converted into.
source§

fn into_deserializer(\n self\n) -> <HashMap<K, V, S> as IntoDeserializer<'de, E>>::Deserializer

Convert this value into a deserializer.
","IntoDeserializer<'de, E>","ironcalc_base::types::SheetData"],["
source§

impl<K, V, H> Serialize for HashMap<K, V, H>where\n K: Serialize,\n V: Serialize,

source§

fn serialize<S>(\n &self,\n serializer: S\n) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>where\n S: Serializer,

Serialize this value into the given Serde serializer. Read more
","Serialize","ironcalc_base::types::SheetData"],["
source§

impl<'de, K, V, S> Deserialize<'de> for HashMap<K, V, S>where\n K: Deserialize<'de> + Eq + Hash,\n V: Deserialize<'de>,\n S: BuildHasher + Default,

source§

fn deserialize<D>(\n deserializer: D\n) -> Result<HashMap<K, V, S>, <D as Deserializer<'de>>::Error>where\n D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
","Deserialize<'de>","ironcalc_base::types::SheetData"]] +};if (window.register_type_impls) {window.register_type_impls(type_impls);} else {window.pending_type_impls = type_impls;}})() \ No newline at end of file