From f041daa050c0ee8a1492a36070b48abc55c96a31 Mon Sep 17 00:00:00 2001 From: nhatcher Date: Wed, 7 Feb 2024 19:11:37 +0000 Subject: [PATCH] deploy: 07c8e1af816f55ffd442c38abd9bf2717bb4cf25 --- .lock | 0 .nojekyll | 0 crates.js | 1 + help.html | 1 + ironcalc/all.html | 1 + ironcalc/compare/fn.compare.html | 5 + ironcalc/compare/fn.test_file.html | 2 + ironcalc/compare/fn.test_load_and_saving.html | 5 + ironcalc/compare/index.html | 1 + ironcalc/compare/sidebar-items.js | 1 + ironcalc/compare/struct.CompareError.html | 12 + ironcalc/compare/struct.Diff.html | 19 + ironcalc/error/enum.XlsxError.html | 23 + ironcalc/error/index.html | 1 + ironcalc/error/sidebar-items.js | 1 + ironcalc/export/fn.save_to_json.html | 2 + ironcalc/export/fn.save_to_xlsx.html | 2 + ironcalc/export/fn.save_xlsx_to_writer.html | 4 + ironcalc/export/index.html | 1 + ironcalc/export/sidebar-items.js | 1 + ironcalc/import/fn.load_from_excel.html | 6 + ironcalc/import/fn.load_model_from_xlsx.html | 5 + ironcalc/import/index.html | 1 + ironcalc/import/sidebar-items.js | 1 + ironcalc/index.html | 47 + ironcalc/sidebar-items.js | 1 + ironcalc_base/all.html | 1 + ironcalc_base/calc_result/index.html | 1 + ironcalc_base/calc_result/sidebar-items.js | 1 + .../calc_result/struct.CellReference.html | 19 + ironcalc_base/calc_result/struct.Range.html | 16 + ironcalc_base/cell/enum.CellValue.html | 23 + ironcalc_base/cell/index.html | 1 + ironcalc_base/cell/sidebar-items.js | 1 + ironcalc_base/expressions/index.html | 1 + .../expressions/lexer/enum.LexerMode.html | 18 + ironcalc_base/expressions/lexer/index.html | 33 + .../expressions/lexer/sidebar-items.js | 1 + .../expressions/lexer/struct.Lexer.html | 31 + .../expressions/lexer/struct.LexerError.html | 18 + .../expressions/lexer/util/fn.get_tokens.html | 31 + .../expressions/lexer/util/index.html | 1 + .../expressions/lexer/util/sidebar-items.js | 1 + .../lexer/util/struct.MarkedToken.html | 20 + .../expressions/parser/enum.Node.html | 105 + ironcalc_base/expressions/parser/index.html | 24 + .../parser/move_formula/index.html | 1 + .../parser/move_formula/sidebar-items.js | 1 + .../expressions/parser/sidebar-items.js | 1 + .../parser/stringify/enum.DisplaceData.html | 41 + .../parser/stringify/fn.to_excel_string.html | 1 + .../parser/stringify/fn.to_rc_format.html | 1 + .../parser/stringify/fn.to_string.html | 1 + .../stringify/fn.to_string_displaced.html | 5 + .../expressions/parser/stringify/index.html | 1 + .../parser/stringify/sidebar-items.js | 1 + .../expressions/parser/struct.Parser.html | 17 + .../expressions/parser/walk/index.html | 1 + .../expressions/parser/walk/sidebar-items.js | 1 + ironcalc_base/expressions/sidebar-items.js | 1 + .../expressions/token/enum.Error.html | 39 + .../expressions/token/enum.OpCompare.html | 23 + .../expressions/token/enum.OpProduct.html | 19 + .../expressions/token/enum.OpSum.html | 19 + .../expressions/token/enum.OpUnary.html | 19 + .../token/enum.TableReference.html | 18 + .../token/enum.TableSpecifier.html | 21 + .../expressions/token/enum.TokenType.html | 57 + .../token/fn.get_error_by_english_name.html | 1 + .../token/fn.get_error_by_name.html | 1 + ironcalc_base/expressions/token/fn.index.html | 1 + .../token/fn.is_english_error_string.html | 1 + ironcalc_base/expressions/token/index.html | 2 + .../expressions/token/sidebar-items.js | 1 + ironcalc_base/expressions/types/index.html | 3 + .../expressions/types/sidebar-items.js | 1 + .../expressions/types/struct.Area.html | 21 + .../types/struct.CellReference.html | 16 + .../types/struct.CellReferenceIndex.html | 22 + .../types/struct.CellReferenceRC.html | 17 + .../expressions/types/struct.ParsedRange.html | 18 + .../types/struct.ParsedReference.html | 23 + .../utils/fn.column_to_number.html | 2 + .../expressions/utils/fn.is_valid_column.html | 1 + .../utils/fn.is_valid_column_number.html | 2 + .../utils/fn.is_valid_identifier.html | 1 + .../expressions/utils/fn.is_valid_row.html | 1 + .../utils/fn.number_to_column.html | 2 + .../utils/fn.parse_reference_a1.html | 1 + .../utils/fn.parse_reference_r1c1.html | 1 + .../expressions/utils/fn.quote_name.html | 3 + ironcalc_base/expressions/utils/index.html | 2 + .../expressions/utils/sidebar-items.js | 1 + .../dates/fn.date_to_serial_number.html | 5 + .../formatter/dates/fn.from_excel_date.html | 1 + ironcalc_base/formatter/dates/index.html | 1 + .../formatter/dates/sidebar-items.js | 1 + .../formatter/format/fn.format_number.html | 5 + ironcalc_base/formatter/format/index.html | 1 + .../formatter/format/sidebar-items.js | 1 + .../formatter/format/struct.Formatted.html | 16 + ironcalc_base/formatter/index.html | 1 + .../formatter/lexer/enum.Compare.html | 20 + ironcalc_base/formatter/lexer/enum.Token.html | 45 + .../fn.is_likely_date_number_format.html | 1 + ironcalc_base/formatter/lexer/index.html | 1 + .../formatter/lexer/sidebar-items.js | 1 + .../formatter/lexer/struct.Lexer.html | 12 + .../formatter/parser/enum.ParsePart.html | 17 + .../formatter/parser/enum.TextToken.html | 31 + ironcalc_base/formatter/parser/index.html | 1 + .../formatter/parser/sidebar-items.js | 1 + .../formatter/parser/struct.DatePart.html | 15 + .../formatter/parser/struct.Digit.html | 16 + .../formatter/parser/struct.ErrorPart.html | 12 + .../formatter/parser/struct.GeneralPart.html | 12 + .../formatter/parser/struct.NumberPart.html | 24 + .../formatter/parser/struct.Parser.html | 15 + ironcalc_base/formatter/sidebar-items.js | 1 + ironcalc_base/index.html | 2 + ironcalc_base/language/fn.get_language.html | 1 + ironcalc_base/language/index.html | 1 + ironcalc_base/language/sidebar-items.js | 1 + ironcalc_base/language/struct.Booleans.html | 19 + ironcalc_base/language/struct.Errors.html | 29 + ironcalc_base/language/struct.Language.html | 19 + ironcalc_base/locale/fn.get_locale.html | 1 + ironcalc_base/locale/fn.get_locale_fix.html | 1 + ironcalc_base/locale/index.html | 1 + ironcalc_base/locale/sidebar-items.js | 1 + ironcalc_base/locale/struct.Currency.html | 19 + .../locale/struct.CurrencyFormats.html | 23 + ironcalc_base/locale/struct.Dates.html | 22 + .../locale/struct.DecimalFormats.html | 18 + ironcalc_base/locale/struct.Locale.html | 20 + .../locale/struct.NumbersProperties.html | 20 + .../locale/struct.NumbersSymbols.html | 30 + ironcalc_base/model/enum.CellState.html | 16 + .../model/enum.ParsedDefinedName.html | 17 + ironcalc_base/model/enum.Tz.html | 1266 ++++++ .../fn.get_milliseconds_since_epoch.html | 1 + ironcalc_base/model/index.html | 6 + ironcalc_base/model/sidebar-items.js | 1 + ironcalc_base/model/struct.CellIndex.html | 16 + ironcalc_base/model/struct.Model.html | 308 ++ ironcalc_base/model/struct.Style.html | 25 + .../new_empty/constant.APPLICATION.html | 1 + .../new_empty/constant.APP_VERSION.html | 1 + .../new_empty/constant.IRONCALC_USER.html | 1 + ironcalc_base/new_empty/enum.Tz.html | 1266 ++++++ ironcalc_base/new_empty/index.html | 1 + ironcalc_base/new_empty/sidebar-items.js | 1 + .../number_format/fn.format_number.html | 1 + .../fn.get_default_num_fmt_id.html | 1 + .../fn.get_new_num_fmt_index.html | 1 + .../number_format/fn.get_num_fmt.html | 1 + .../fn.to_excel_precision_str.html | 9 + .../number_format/fn.to_precision.html | 1 + .../number_format/fn.to_precision_str.html | 1 + ironcalc_base/number_format/index.html | 1 + ironcalc_base/number_format/sidebar-items.js | 1 + ironcalc_base/sidebar-items.js | 1 + ironcalc_base/types/enum.BorderStyle.html | 29 + ironcalc_base/types/enum.Cell.html | 76 + ironcalc_base/types/enum.CellType.html | 22 + ironcalc_base/types/enum.FontScheme.html | 23 + .../types/enum.HorizontalAlignment.html | 28 + ironcalc_base/types/enum.SheetState.html | 29 + .../types/enum.VerticalAlignment.html | 25 + ironcalc_base/types/index.html | 3 + ironcalc_base/types/sidebar-items.js | 1 + ironcalc_base/types/struct.Alignment.html | 22 + ironcalc_base/types/struct.Border.html | 26 + ironcalc_base/types/struct.BorderItem.html | 21 + ironcalc_base/types/struct.CellStyleXfs.html | 29 + ironcalc_base/types/struct.CellStyles.html | 22 + ironcalc_base/types/struct.CellXfs.html | 32 + ironcalc_base/types/struct.Col.html | 26 + ironcalc_base/types/struct.Comment.html | 23 + ironcalc_base/types/struct.DefinedName.html | 23 + ironcalc_base/types/struct.Fill.html | 22 + ironcalc_base/types/struct.Font.html | 28 + ironcalc_base/types/struct.Metadata.html | 25 + ironcalc_base/types/struct.NumFmt.html | 21 + ironcalc_base/types/struct.Row.html | 26 + ironcalc_base/types/struct.SheetInfo.html | 24 + ironcalc_base/types/struct.Styles.html | 36 + ironcalc_base/types/struct.Table.html | 31 + ironcalc_base/types/struct.TableColumn.html | 26 + .../types/struct.TableStyleInfo.html | 24 + ironcalc_base/types/struct.Workbook.html | 31 + .../types/struct.WorkbookSettings.html | 21 + ironcalc_base/types/struct.Worksheet.html | 115 + ironcalc_base/types/type.SheetData.html | 3 + .../worksheet/enum.NavigationDirection.html | 20 + ironcalc_base/worksheet/index.html | 1 + ironcalc_base/worksheet/sidebar-items.js | 1 + .../worksheet/struct.WorksheetDimension.html | 19 + search-index.js | 7 + settings.html | 1 + src-files.js | 6 + src/ironcalc/compare.rs.html | 413 ++ src/ironcalc/error.rs.html | 179 + src/ironcalc/export/_rels.rs.html | 13 + src/ironcalc/export/doc_props.rs.html | 139 + src/ironcalc/export/escape.rs.html | 199 + src/ironcalc/export/mod.rs.html | 275 ++ src/ironcalc/export/shared_strings.rs.html | 33 + src/ironcalc/export/styles.rs.html | 565 +++ src/ironcalc/export/workbook.rs.html | 183 + src/ironcalc/export/workbook_xml_rels.rs.html | 51 + src/ironcalc/export/worksheets.rs.html | 535 +++ src/ironcalc/export/xml_constants.rs.html | 11 + src/ironcalc/import/colors.rs.html | 515 +++ src/ironcalc/import/metadata.rs.html | 163 + src/ironcalc/import/mod.rs.html | 249 ++ src/ironcalc/import/shared_strings.rs.html | 161 + src/ironcalc/import/styles.rs.html | 773 ++++ src/ironcalc/import/tables.rs.html | 431 ++ src/ironcalc/import/util.rs.html | 157 + src/ironcalc/import/workbook.rs.html | 159 + src/ironcalc/import/worksheets.rs.html | 1851 ++++++++ src/ironcalc/lib.rs.html | 123 + src/ironcalc_base/actions.rs.html | 715 ++++ src/ironcalc_base/calc_result.rs.html | 229 + src/ironcalc_base/cast.rs.html | 429 ++ src/ironcalc_base/cell.rs.html | 385 ++ src/ironcalc_base/constants.rs.html | 33 + src/ironcalc_base/diffs.rs.html | 273 ++ .../expressions/lexer/mod.rs.html | 1525 +++++++ .../expressions/lexer/ranges.rs.html | 639 +++ .../lexer/structured_references.rs.html | 377 ++ .../expressions/lexer/util.rs.html | 171 + src/ironcalc_base/expressions/mod.rs.html | 13 + .../expressions/parser/mod.rs.html | 1755 ++++++++ .../expressions/parser/move_formula.rs.html | 795 ++++ .../expressions/parser/stringify.rs.html | 1225 ++++++ .../expressions/parser/walk.rs.html | 553 +++ src/ironcalc_base/expressions/token.rs.html | 777 ++++ src/ironcalc_base/expressions/types.rs.html | 103 + .../expressions/utils/mod.rs.html | 563 +++ src/ironcalc_base/formatter/dates.rs.html | 35 + src/ironcalc_base/formatter/format.rs.html | 1527 +++++++ src/ironcalc_base/formatter/lexer.rs.html | 817 ++++ src/ironcalc_base/formatter/mod.rs.html | 211 + src/ironcalc_base/formatter/parser.rs.html | 595 +++ .../functions/binary_search.rs.html | 421 ++ .../functions/date_and_time.rs.html | 629 +++ .../functions/engineering/bessel.rs.html | 353 ++ .../engineering/bit_operations.rs.html | 467 ++ .../functions/engineering/complex.rs.html | 1587 +++++++ .../functions/engineering/convert.rs.html | 837 ++++ .../functions/engineering/misc.rs.html | 119 + .../functions/engineering/mod.rs.html | 15 + .../engineering/number_basis.rs.html | 1093 +++++ .../transcendental/bessel_i.rs.html | 289 ++ .../transcendental/bessel_j0_y0.rs.html | 805 ++++ .../transcendental/bessel_j1_y1.rs.html | 783 ++++ .../transcendental/bessel_jn_yn.rs.html | 659 +++ .../transcendental/bessel_k.rs.html | 181 + .../transcendental/bessel_util.rs.html | 39 + .../engineering/transcendental/erf.rs.html | 107 + .../engineering/transcendental/mod.rs.html | 33 + src/ironcalc_base/functions/financial.rs.html | 3769 +++++++++++++++++ .../functions/financial_util.rs.html | 511 +++ .../functions/information.rs.html | 593 +++ src/ironcalc_base/functions/logical.rs.html | 643 +++ .../functions/lookup_and_reference.rs.html | 1687 ++++++++ .../functions/mathematical.rs.html | 1343 ++++++ src/ironcalc_base/functions/mod.rs.html | 1861 ++++++++ .../functions/statistical.rs.html | 1249 ++++++ src/ironcalc_base/functions/subtotal.rs.html | 1169 +++++ src/ironcalc_base/functions/text.rs.html | 2209 ++++++++++ src/ironcalc_base/functions/text_util.rs.html | 393 ++ src/ironcalc_base/functions/util.rs.html | 803 ++++ src/ironcalc_base/functions/xlookup.rs.html | 769 ++++ .../implicit_intersection.rs.html | 97 + src/ironcalc_base/language/mod.rs.html | 93 + src/ironcalc_base/lib.rs.html | 65 + src/ironcalc_base/locale/mod.rs.html | 187 + src/ironcalc_base/model.rs.html | 3147 ++++++++++++++ src/ironcalc_base/new_empty.rs.html | 793 ++++ src/ironcalc_base/number_format.rs.html | 315 ++ src/ironcalc_base/styles.rs.html | 583 +++ src/ironcalc_base/types.rs.html | 1327 ++++++ src/ironcalc_base/units.rs.html | 729 ++++ src/ironcalc_base/utils.rs.html | 739 ++++ src/ironcalc_base/workbook.rs.html | 85 + src/ironcalc_base/worksheet.rs.html | 1261 ++++++ src/test/test.rs.html | 69 + static.files/COPYRIGHT-23e9bde6c69aea69.txt | 50 + .../FiraSans-LICENSE-db4b642586e02d97.txt | 98 + .../FiraSans-Medium-8f9a781e4970d388.woff2 | Bin 0 -> 132780 bytes .../FiraSans-Regular-018c141bf0843ffd.woff2 | Bin 0 -> 129188 bytes .../LICENSE-APACHE-b91fa81cba47b86a.txt | 201 + static.files/LICENSE-MIT-65090b722b3f6c56.txt | 23 + ...anumBarunGothic-0f09457c7a19b7c6.ttf.woff2 | Bin 0 -> 399468 bytes ...umBarunGothic-LICENSE-18c5adf4b52b4041.txt | 103 + ...ourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 | Bin 0 -> 44896 bytes ...SourceCodePro-LICENSE-d180d465a756484a.txt | 97 + ...CodePro-Regular-562dcc5011b6de7d.ttf.woff2 | Bin 0 -> 52228 bytes ...odePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 | Bin 0 -> 52348 bytes ...urceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 | Bin 0 -> 81540 bytes ...SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 | Bin 0 -> 59716 bytes .../SourceSerif4-LICENSE-3bb119e13b1258b7.md | 98 + ...eSerif4-Regular-46f98efaafac5295.ttf.woff2 | Bin 0 -> 76260 bytes static.files/clipboard-7571035ce49a181d.svg | 1 + .../favicon-16x16-8b506e7a72182f1c.png | Bin 0 -> 715 bytes static.files/favicon-2c020d218678b618.svg | 24 + .../favicon-32x32-422f7d1d52889060.png | Bin 0 -> 1125 bytes static.files/main-9dd44ab47b99a0fb.js | 12 + static.files/normalize-76eba96aa4d2e634.css | 2 + static.files/noscript-5d8b3c7633ad77ba.css | 1 + static.files/rust-logo-151179464ae7ed46.svg | 61 + static.files/rustdoc-9ee3a5e31a2afa3e.css | 10 + .../scrape-examples-ef1e698c1d417c0c.js | 1 + static.files/search-8fbf244ebcf71464.js | 5 + static.files/settings-74424d7eec62a23e.js | 17 + static.files/src-script-3280b574d94e47b4.js | 1 + static.files/storage-fec3eaa3851e447d.js | 1 + static.files/wheel-7b819b6101059cd0.svg | 1 + test/all.html | 1 + test/fn.main.html | 1 + test/index.html | 8 + test/sidebar-items.js | 1 + trait.impl/core/clone/trait.Clone.js | 3 + trait.impl/core/cmp/trait.Eq.js | 4 + trait.impl/core/cmp/trait.PartialEq.js | 4 + trait.impl/core/convert/trait.From.js | 4 + trait.impl/core/default/trait.Default.js | 3 + trait.impl/core/error/trait.Error.js | 3 + trait.impl/core/fmt/trait.Debug.js | 4 + trait.impl/core/fmt/trait.Display.js | 4 + trait.impl/core/marker/trait.Copy.js | 3 + trait.impl/core/marker/trait.Freeze.js | 4 + trait.impl/core/marker/trait.Send.js | 4 + trait.impl/core/marker/trait.StructuralEq.js | 4 + .../core/marker/trait.StructuralPartialEq.js | 4 + trait.impl/core/marker/trait.Sync.js | 4 + trait.impl/core/marker/trait.Unpin.js | 4 + .../panic/unwind_safe/trait.RefUnwindSafe.js | 4 + .../panic/unwind_safe/trait.UnwindSafe.js | 4 + trait.impl/serde/de/trait.Deserialize.js | 3 + trait.impl/serde/ser/trait.Serialize.js | 3 + .../collections/hash/map/struct.HashMap.js | 3 + 345 files changed, 62023 insertions(+) create mode 100644 .lock create mode 100644 .nojekyll create mode 100644 crates.js create mode 100644 help.html create mode 100644 ironcalc/all.html create mode 100644 ironcalc/compare/fn.compare.html create mode 100644 ironcalc/compare/fn.test_file.html create mode 100644 ironcalc/compare/fn.test_load_and_saving.html create mode 100644 ironcalc/compare/index.html create mode 100644 ironcalc/compare/sidebar-items.js create mode 100644 ironcalc/compare/struct.CompareError.html create mode 100644 ironcalc/compare/struct.Diff.html create mode 100644 ironcalc/error/enum.XlsxError.html create mode 100644 ironcalc/error/index.html create mode 100644 ironcalc/error/sidebar-items.js create mode 100644 ironcalc/export/fn.save_to_json.html create mode 100644 ironcalc/export/fn.save_to_xlsx.html create mode 100644 ironcalc/export/fn.save_xlsx_to_writer.html create mode 100644 ironcalc/export/index.html create mode 100644 ironcalc/export/sidebar-items.js create mode 100644 ironcalc/import/fn.load_from_excel.html create mode 100644 ironcalc/import/fn.load_model_from_xlsx.html create mode 100644 ironcalc/import/index.html create mode 100644 ironcalc/import/sidebar-items.js create mode 100644 ironcalc/index.html create mode 100644 ironcalc/sidebar-items.js create mode 100644 ironcalc_base/all.html create mode 100644 ironcalc_base/calc_result/index.html create mode 100644 ironcalc_base/calc_result/sidebar-items.js create mode 100644 ironcalc_base/calc_result/struct.CellReference.html create mode 100644 ironcalc_base/calc_result/struct.Range.html create mode 100644 ironcalc_base/cell/enum.CellValue.html create mode 100644 ironcalc_base/cell/index.html create mode 100644 ironcalc_base/cell/sidebar-items.js create mode 100644 ironcalc_base/expressions/index.html create mode 100644 ironcalc_base/expressions/lexer/enum.LexerMode.html create mode 100644 ironcalc_base/expressions/lexer/index.html create mode 100644 ironcalc_base/expressions/lexer/sidebar-items.js create mode 100644 ironcalc_base/expressions/lexer/struct.Lexer.html create mode 100644 ironcalc_base/expressions/lexer/struct.LexerError.html create mode 100644 ironcalc_base/expressions/lexer/util/fn.get_tokens.html create mode 100644 ironcalc_base/expressions/lexer/util/index.html create mode 100644 ironcalc_base/expressions/lexer/util/sidebar-items.js create mode 100644 ironcalc_base/expressions/lexer/util/struct.MarkedToken.html create mode 100644 ironcalc_base/expressions/parser/enum.Node.html create mode 100644 ironcalc_base/expressions/parser/index.html create mode 100644 ironcalc_base/expressions/parser/move_formula/index.html create mode 100644 ironcalc_base/expressions/parser/move_formula/sidebar-items.js create mode 100644 ironcalc_base/expressions/parser/sidebar-items.js create mode 100644 ironcalc_base/expressions/parser/stringify/enum.DisplaceData.html create mode 100644 ironcalc_base/expressions/parser/stringify/fn.to_excel_string.html create mode 100644 ironcalc_base/expressions/parser/stringify/fn.to_rc_format.html create mode 100644 ironcalc_base/expressions/parser/stringify/fn.to_string.html create mode 100644 ironcalc_base/expressions/parser/stringify/fn.to_string_displaced.html create mode 100644 ironcalc_base/expressions/parser/stringify/index.html create mode 100644 ironcalc_base/expressions/parser/stringify/sidebar-items.js create mode 100644 ironcalc_base/expressions/parser/struct.Parser.html create mode 100644 ironcalc_base/expressions/parser/walk/index.html create mode 100644 ironcalc_base/expressions/parser/walk/sidebar-items.js create mode 100644 ironcalc_base/expressions/sidebar-items.js create mode 100644 ironcalc_base/expressions/token/enum.Error.html create mode 100644 ironcalc_base/expressions/token/enum.OpCompare.html create mode 100644 ironcalc_base/expressions/token/enum.OpProduct.html create mode 100644 ironcalc_base/expressions/token/enum.OpSum.html create mode 100644 ironcalc_base/expressions/token/enum.OpUnary.html create mode 100644 ironcalc_base/expressions/token/enum.TableReference.html create mode 100644 ironcalc_base/expressions/token/enum.TableSpecifier.html create mode 100644 ironcalc_base/expressions/token/enum.TokenType.html create mode 100644 ironcalc_base/expressions/token/fn.get_error_by_english_name.html create mode 100644 ironcalc_base/expressions/token/fn.get_error_by_name.html create mode 100644 ironcalc_base/expressions/token/fn.index.html create mode 100644 ironcalc_base/expressions/token/fn.is_english_error_string.html create mode 100644 ironcalc_base/expressions/token/index.html create mode 100644 ironcalc_base/expressions/token/sidebar-items.js create mode 100644 ironcalc_base/expressions/types/index.html create mode 100644 ironcalc_base/expressions/types/sidebar-items.js create mode 100644 ironcalc_base/expressions/types/struct.Area.html create mode 100644 ironcalc_base/expressions/types/struct.CellReference.html create mode 100644 ironcalc_base/expressions/types/struct.CellReferenceIndex.html create mode 100644 ironcalc_base/expressions/types/struct.CellReferenceRC.html create mode 100644 ironcalc_base/expressions/types/struct.ParsedRange.html create mode 100644 ironcalc_base/expressions/types/struct.ParsedReference.html create mode 100644 ironcalc_base/expressions/utils/fn.column_to_number.html create mode 100644 ironcalc_base/expressions/utils/fn.is_valid_column.html create mode 100644 ironcalc_base/expressions/utils/fn.is_valid_column_number.html create mode 100644 ironcalc_base/expressions/utils/fn.is_valid_identifier.html create mode 100644 ironcalc_base/expressions/utils/fn.is_valid_row.html create mode 100644 ironcalc_base/expressions/utils/fn.number_to_column.html create mode 100644 ironcalc_base/expressions/utils/fn.parse_reference_a1.html create mode 100644 ironcalc_base/expressions/utils/fn.parse_reference_r1c1.html create mode 100644 ironcalc_base/expressions/utils/fn.quote_name.html create mode 100644 ironcalc_base/expressions/utils/index.html create mode 100644 ironcalc_base/expressions/utils/sidebar-items.js create mode 100644 ironcalc_base/formatter/dates/fn.date_to_serial_number.html create mode 100644 ironcalc_base/formatter/dates/fn.from_excel_date.html create mode 100644 ironcalc_base/formatter/dates/index.html create mode 100644 ironcalc_base/formatter/dates/sidebar-items.js create mode 100644 ironcalc_base/formatter/format/fn.format_number.html create mode 100644 ironcalc_base/formatter/format/index.html create mode 100644 ironcalc_base/formatter/format/sidebar-items.js create mode 100644 ironcalc_base/formatter/format/struct.Formatted.html create mode 100644 ironcalc_base/formatter/index.html create mode 100644 ironcalc_base/formatter/lexer/enum.Compare.html create mode 100644 ironcalc_base/formatter/lexer/enum.Token.html create mode 100644 ironcalc_base/formatter/lexer/fn.is_likely_date_number_format.html create mode 100644 ironcalc_base/formatter/lexer/index.html create mode 100644 ironcalc_base/formatter/lexer/sidebar-items.js create mode 100644 ironcalc_base/formatter/lexer/struct.Lexer.html create mode 100644 ironcalc_base/formatter/parser/enum.ParsePart.html create mode 100644 ironcalc_base/formatter/parser/enum.TextToken.html create mode 100644 ironcalc_base/formatter/parser/index.html create mode 100644 ironcalc_base/formatter/parser/sidebar-items.js create mode 100644 ironcalc_base/formatter/parser/struct.DatePart.html create mode 100644 ironcalc_base/formatter/parser/struct.Digit.html create mode 100644 ironcalc_base/formatter/parser/struct.ErrorPart.html create mode 100644 ironcalc_base/formatter/parser/struct.GeneralPart.html create mode 100644 ironcalc_base/formatter/parser/struct.NumberPart.html create mode 100644 ironcalc_base/formatter/parser/struct.Parser.html create mode 100644 ironcalc_base/formatter/sidebar-items.js create mode 100644 ironcalc_base/index.html create mode 100644 ironcalc_base/language/fn.get_language.html create mode 100644 ironcalc_base/language/index.html create mode 100644 ironcalc_base/language/sidebar-items.js create mode 100644 ironcalc_base/language/struct.Booleans.html create mode 100644 ironcalc_base/language/struct.Errors.html create mode 100644 ironcalc_base/language/struct.Language.html create mode 100644 ironcalc_base/locale/fn.get_locale.html create mode 100644 ironcalc_base/locale/fn.get_locale_fix.html create mode 100644 ironcalc_base/locale/index.html create mode 100644 ironcalc_base/locale/sidebar-items.js create mode 100644 ironcalc_base/locale/struct.Currency.html create mode 100644 ironcalc_base/locale/struct.CurrencyFormats.html create mode 100644 ironcalc_base/locale/struct.Dates.html create mode 100644 ironcalc_base/locale/struct.DecimalFormats.html create mode 100644 ironcalc_base/locale/struct.Locale.html create mode 100644 ironcalc_base/locale/struct.NumbersProperties.html create mode 100644 ironcalc_base/locale/struct.NumbersSymbols.html create mode 100644 ironcalc_base/model/enum.CellState.html create mode 100644 ironcalc_base/model/enum.ParsedDefinedName.html create mode 100644 ironcalc_base/model/enum.Tz.html create mode 100644 ironcalc_base/model/fn.get_milliseconds_since_epoch.html create mode 100644 ironcalc_base/model/index.html create mode 100644 ironcalc_base/model/sidebar-items.js create mode 100644 ironcalc_base/model/struct.CellIndex.html create mode 100644 ironcalc_base/model/struct.Model.html create mode 100644 ironcalc_base/model/struct.Style.html create mode 100644 ironcalc_base/new_empty/constant.APPLICATION.html create mode 100644 ironcalc_base/new_empty/constant.APP_VERSION.html create mode 100644 ironcalc_base/new_empty/constant.IRONCALC_USER.html create mode 100644 ironcalc_base/new_empty/enum.Tz.html create mode 100644 ironcalc_base/new_empty/index.html create mode 100644 ironcalc_base/new_empty/sidebar-items.js create mode 100644 ironcalc_base/number_format/fn.format_number.html create mode 100644 ironcalc_base/number_format/fn.get_default_num_fmt_id.html create mode 100644 ironcalc_base/number_format/fn.get_new_num_fmt_index.html create mode 100644 ironcalc_base/number_format/fn.get_num_fmt.html create mode 100644 ironcalc_base/number_format/fn.to_excel_precision_str.html create mode 100644 ironcalc_base/number_format/fn.to_precision.html create mode 100644 ironcalc_base/number_format/fn.to_precision_str.html create mode 100644 ironcalc_base/number_format/index.html create mode 100644 ironcalc_base/number_format/sidebar-items.js create mode 100644 ironcalc_base/sidebar-items.js create mode 100644 ironcalc_base/types/enum.BorderStyle.html create mode 100644 ironcalc_base/types/enum.Cell.html create mode 100644 ironcalc_base/types/enum.CellType.html create mode 100644 ironcalc_base/types/enum.FontScheme.html create mode 100644 ironcalc_base/types/enum.HorizontalAlignment.html create mode 100644 ironcalc_base/types/enum.SheetState.html create mode 100644 ironcalc_base/types/enum.VerticalAlignment.html create mode 100644 ironcalc_base/types/index.html create mode 100644 ironcalc_base/types/sidebar-items.js create mode 100644 ironcalc_base/types/struct.Alignment.html create mode 100644 ironcalc_base/types/struct.Border.html create mode 100644 ironcalc_base/types/struct.BorderItem.html create mode 100644 ironcalc_base/types/struct.CellStyleXfs.html create mode 100644 ironcalc_base/types/struct.CellStyles.html create mode 100644 ironcalc_base/types/struct.CellXfs.html create mode 100644 ironcalc_base/types/struct.Col.html create mode 100644 ironcalc_base/types/struct.Comment.html create mode 100644 ironcalc_base/types/struct.DefinedName.html create mode 100644 ironcalc_base/types/struct.Fill.html create mode 100644 ironcalc_base/types/struct.Font.html create mode 100644 ironcalc_base/types/struct.Metadata.html create mode 100644 ironcalc_base/types/struct.NumFmt.html create mode 100644 ironcalc_base/types/struct.Row.html create mode 100644 ironcalc_base/types/struct.SheetInfo.html create mode 100644 ironcalc_base/types/struct.Styles.html create mode 100644 ironcalc_base/types/struct.Table.html create mode 100644 ironcalc_base/types/struct.TableColumn.html create mode 100644 ironcalc_base/types/struct.TableStyleInfo.html create mode 100644 ironcalc_base/types/struct.Workbook.html create mode 100644 ironcalc_base/types/struct.WorkbookSettings.html create mode 100644 ironcalc_base/types/struct.Worksheet.html create mode 100644 ironcalc_base/types/type.SheetData.html create mode 100644 ironcalc_base/worksheet/enum.NavigationDirection.html create mode 100644 ironcalc_base/worksheet/index.html create mode 100644 ironcalc_base/worksheet/sidebar-items.js create mode 100644 ironcalc_base/worksheet/struct.WorksheetDimension.html create mode 100644 search-index.js create mode 100644 settings.html create mode 100644 src-files.js create mode 100644 src/ironcalc/compare.rs.html create mode 100644 src/ironcalc/error.rs.html create mode 100644 src/ironcalc/export/_rels.rs.html create mode 100644 src/ironcalc/export/doc_props.rs.html create mode 100644 src/ironcalc/export/escape.rs.html create mode 100644 src/ironcalc/export/mod.rs.html create mode 100644 src/ironcalc/export/shared_strings.rs.html create mode 100644 src/ironcalc/export/styles.rs.html create mode 100644 src/ironcalc/export/workbook.rs.html create mode 100644 src/ironcalc/export/workbook_xml_rels.rs.html create mode 100644 src/ironcalc/export/worksheets.rs.html create mode 100644 src/ironcalc/export/xml_constants.rs.html create mode 100644 src/ironcalc/import/colors.rs.html create mode 100644 src/ironcalc/import/metadata.rs.html create mode 100644 src/ironcalc/import/mod.rs.html create mode 100644 src/ironcalc/import/shared_strings.rs.html create mode 100644 src/ironcalc/import/styles.rs.html create mode 100644 src/ironcalc/import/tables.rs.html create mode 100644 src/ironcalc/import/util.rs.html create mode 100644 src/ironcalc/import/workbook.rs.html create mode 100644 src/ironcalc/import/worksheets.rs.html create mode 100644 src/ironcalc/lib.rs.html create mode 100644 src/ironcalc_base/actions.rs.html create mode 100644 src/ironcalc_base/calc_result.rs.html create mode 100644 src/ironcalc_base/cast.rs.html create mode 100644 src/ironcalc_base/cell.rs.html create mode 100644 src/ironcalc_base/constants.rs.html create mode 100644 src/ironcalc_base/diffs.rs.html create mode 100644 src/ironcalc_base/expressions/lexer/mod.rs.html create mode 100644 src/ironcalc_base/expressions/lexer/ranges.rs.html create mode 100644 src/ironcalc_base/expressions/lexer/structured_references.rs.html create mode 100644 src/ironcalc_base/expressions/lexer/util.rs.html create mode 100644 src/ironcalc_base/expressions/mod.rs.html create mode 100644 src/ironcalc_base/expressions/parser/mod.rs.html create mode 100644 src/ironcalc_base/expressions/parser/move_formula.rs.html create mode 100644 src/ironcalc_base/expressions/parser/stringify.rs.html create mode 100644 src/ironcalc_base/expressions/parser/walk.rs.html create mode 100644 src/ironcalc_base/expressions/token.rs.html create mode 100644 src/ironcalc_base/expressions/types.rs.html create mode 100644 src/ironcalc_base/expressions/utils/mod.rs.html create mode 100644 src/ironcalc_base/formatter/dates.rs.html create mode 100644 src/ironcalc_base/formatter/format.rs.html create mode 100644 src/ironcalc_base/formatter/lexer.rs.html create mode 100644 src/ironcalc_base/formatter/mod.rs.html create mode 100644 src/ironcalc_base/formatter/parser.rs.html create mode 100644 src/ironcalc_base/functions/binary_search.rs.html create mode 100644 src/ironcalc_base/functions/date_and_time.rs.html create mode 100644 src/ironcalc_base/functions/engineering/bessel.rs.html create mode 100644 src/ironcalc_base/functions/engineering/bit_operations.rs.html create mode 100644 src/ironcalc_base/functions/engineering/complex.rs.html create mode 100644 src/ironcalc_base/functions/engineering/convert.rs.html create mode 100644 src/ironcalc_base/functions/engineering/misc.rs.html create mode 100644 src/ironcalc_base/functions/engineering/mod.rs.html create mode 100644 src/ironcalc_base/functions/engineering/number_basis.rs.html create mode 100644 src/ironcalc_base/functions/engineering/transcendental/bessel_i.rs.html create mode 100644 src/ironcalc_base/functions/engineering/transcendental/bessel_j0_y0.rs.html create mode 100644 src/ironcalc_base/functions/engineering/transcendental/bessel_j1_y1.rs.html create mode 100644 src/ironcalc_base/functions/engineering/transcendental/bessel_jn_yn.rs.html create mode 100644 src/ironcalc_base/functions/engineering/transcendental/bessel_k.rs.html create mode 100644 src/ironcalc_base/functions/engineering/transcendental/bessel_util.rs.html create mode 100644 src/ironcalc_base/functions/engineering/transcendental/erf.rs.html create mode 100644 src/ironcalc_base/functions/engineering/transcendental/mod.rs.html create mode 100644 src/ironcalc_base/functions/financial.rs.html create mode 100644 src/ironcalc_base/functions/financial_util.rs.html create mode 100644 src/ironcalc_base/functions/information.rs.html create mode 100644 src/ironcalc_base/functions/logical.rs.html create mode 100644 src/ironcalc_base/functions/lookup_and_reference.rs.html create mode 100644 src/ironcalc_base/functions/mathematical.rs.html create mode 100644 src/ironcalc_base/functions/mod.rs.html create mode 100644 src/ironcalc_base/functions/statistical.rs.html create mode 100644 src/ironcalc_base/functions/subtotal.rs.html create mode 100644 src/ironcalc_base/functions/text.rs.html create mode 100644 src/ironcalc_base/functions/text_util.rs.html create mode 100644 src/ironcalc_base/functions/util.rs.html create mode 100644 src/ironcalc_base/functions/xlookup.rs.html create mode 100644 src/ironcalc_base/implicit_intersection.rs.html create mode 100644 src/ironcalc_base/language/mod.rs.html create mode 100644 src/ironcalc_base/lib.rs.html create mode 100644 src/ironcalc_base/locale/mod.rs.html create mode 100644 src/ironcalc_base/model.rs.html create mode 100644 src/ironcalc_base/new_empty.rs.html create mode 100644 src/ironcalc_base/number_format.rs.html create mode 100644 src/ironcalc_base/styles.rs.html create mode 100644 src/ironcalc_base/types.rs.html create mode 100644 src/ironcalc_base/units.rs.html create mode 100644 src/ironcalc_base/utils.rs.html create mode 100644 src/ironcalc_base/workbook.rs.html create mode 100644 src/ironcalc_base/worksheet.rs.html create mode 100644 src/test/test.rs.html create mode 100644 static.files/COPYRIGHT-23e9bde6c69aea69.txt create mode 100644 static.files/FiraSans-LICENSE-db4b642586e02d97.txt create mode 100644 static.files/FiraSans-Medium-8f9a781e4970d388.woff2 create mode 100644 static.files/FiraSans-Regular-018c141bf0843ffd.woff2 create mode 100644 static.files/LICENSE-APACHE-b91fa81cba47b86a.txt create mode 100644 static.files/LICENSE-MIT-65090b722b3f6c56.txt create mode 100644 static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 create mode 100644 static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt create mode 100644 static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 create mode 100644 static.files/SourceCodePro-LICENSE-d180d465a756484a.txt create mode 100644 static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 create mode 100644 static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 create mode 100644 static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 create mode 100644 static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 create mode 100644 static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md create mode 100644 static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 create mode 100644 static.files/clipboard-7571035ce49a181d.svg create mode 100644 static.files/favicon-16x16-8b506e7a72182f1c.png create mode 100644 static.files/favicon-2c020d218678b618.svg create mode 100644 static.files/favicon-32x32-422f7d1d52889060.png create mode 100644 static.files/main-9dd44ab47b99a0fb.js create mode 100644 static.files/normalize-76eba96aa4d2e634.css create mode 100644 static.files/noscript-5d8b3c7633ad77ba.css create mode 100644 static.files/rust-logo-151179464ae7ed46.svg create mode 100644 static.files/rustdoc-9ee3a5e31a2afa3e.css create mode 100644 static.files/scrape-examples-ef1e698c1d417c0c.js create mode 100644 static.files/search-8fbf244ebcf71464.js create mode 100644 static.files/settings-74424d7eec62a23e.js create mode 100644 static.files/src-script-3280b574d94e47b4.js create mode 100644 static.files/storage-fec3eaa3851e447d.js create mode 100644 static.files/wheel-7b819b6101059cd0.svg create mode 100644 test/all.html create mode 100644 test/fn.main.html create mode 100644 test/index.html create mode 100644 test/sidebar-items.js create mode 100644 trait.impl/core/clone/trait.Clone.js create mode 100644 trait.impl/core/cmp/trait.Eq.js create mode 100644 trait.impl/core/cmp/trait.PartialEq.js create mode 100644 trait.impl/core/convert/trait.From.js create mode 100644 trait.impl/core/default/trait.Default.js create mode 100644 trait.impl/core/error/trait.Error.js create mode 100644 trait.impl/core/fmt/trait.Debug.js create mode 100644 trait.impl/core/fmt/trait.Display.js create mode 100644 trait.impl/core/marker/trait.Copy.js create mode 100644 trait.impl/core/marker/trait.Freeze.js create mode 100644 trait.impl/core/marker/trait.Send.js create mode 100644 trait.impl/core/marker/trait.StructuralEq.js create mode 100644 trait.impl/core/marker/trait.StructuralPartialEq.js create mode 100644 trait.impl/core/marker/trait.Sync.js create mode 100644 trait.impl/core/marker/trait.Unpin.js create mode 100644 trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js create mode 100644 trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js create mode 100644 trait.impl/serde/de/trait.Deserialize.js create mode 100644 trait.impl/serde/ser/trait.Serialize.js create mode 100644 type.impl/std/collections/hash/map/struct.HashMap.js 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 0000000000000000000000000000000000000000..7a1e5fc548ef28137a32150b6aa50a568cd53d02 GIT binary patch literal 132780 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�|o|A<_G9#bJ&K$mXTSM@kZfZ^-ZYS)6EG~kHJ{! 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

TIWHPC<_9>em(Cvlhuf-BD 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 literal 0 HcmV?d00001 diff --git a/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 b/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e766e06ccb0d457fcdc8d4428efb796c7772a497 GIT binary patch literal 129188 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

)Thpg9J)x+@B(n?maf9&#`PQNHtKX<@|CQ`> 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 N#!G)s;2JO>~QPMdMZ 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

xz5_8aF@(pQs@zgto+ItjS_s2^}<|M=lFQ2 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,qWUc(1+qXDfx7@=bp~~)(TqS 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 Psah$GwaNdR#Q^{S={DW} literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..462c34efcd9d6b70b42359ca1a1d9476efe43eeb GIT binary patch literal 44896 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 V7)~)yDZnpu49h_gZvJhn{{a>V-DCg& literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..10b558e0b69a74b8329766fffbd5f64356c7230d GIT binary patch literal 52228 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 z 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 D0=ucW literal 0 HcmV?d00001 diff --git a/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 b/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..181a07f63bef8f18e42a5a57463e0e60cf8e8035 GIT binary patch literal 81540 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 literal 0 HcmV?d00001 diff --git a/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 b/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2ae08a7bedfed08cdfea76039c1bb1fa1d6cdf67 GIT binary patch literal 59716 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 tC40Gi`Mi4m<9BpxR%m?w=so8}_Gqj!BM1dG + +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 0000000000000000000000000000000000000000..0263fc304226d90e224e53053855ad138303b70b GIT binary patch literal 76260 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 G0RRB5qhxRZ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ea4b45cae1618e6e20e6d61897da953f34b66b30 GIT binary patch literal 715 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YDR+ueoXe|!I#{XiaP zfk$L9P|ym58ULAS1_A}yOFVsD*`G1;@(QxEEoEaloaX$3oUhmn3B1b>^ zXS8u>Ub4$r*u2F1FEiJ>rQJ&x%nC}Q!>*kacg*^JAVaGgCxj?;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1UT zt3o15f)dLW3X1a6GILTDN-7Id6*3D-k{K8(<~;ty!%-Nfp>fLp^cl~mK@7~w+ + + + + diff --git a/static.files/favicon-32x32-422f7d1d52889060.png b/static.files/favicon-32x32-422f7d1d52889060.png new file mode 100644 index 0000000000000000000000000000000000000000..69b8613ce1506e1c92b864f91cc46df06348c62b GIT binary patch literal 1125 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-G$+Qd;gjJKptm- zM`SV3z!DH1*13q>1OJmp-&A)3@J_A;A0V7u0bkfJ)-`Krrb zGey1(YybBo_r#8#3kPrfo#tA4xb3R$F4j$a9H~9huUPE$JQDstTM~O>^@Ps(;>UH<3Fx0=LBZUre@8723HjVToWun1;~KVtGY7SF1E7liM5< zHa%KaZ1y+v;MF5PX0dXM#bh1)zI}?P`JCclPp)GktoyD%Uv%1HzQp-VwViVJr}FN4 zBVAi3pdsabJ2zzio=sD>mtWX++u%m3k>>5t|1&=?+*B*EnLW)#$^O=9J{D1Fvz#4w zCmkrSML-}_v8Imc2?OP1;|%KWmLM+u&^dKy+fI{C57UY0UhRg-3U_ zKl;3k)jRBCi*uZh#-8L8Gwj!FXV37syULEeYD%&1+S-jgUC&wB|>?y4oO5hW>!C8<`)MX5lF!N|bKNY}tn*U&h` z(Adh*+{(a0+rYrez#!Wq{4a`z-29Zxv`X9>q*C7l^C^QQ$cEtjw370~qEv?R@^Zb* zyzJuS#DY}4{G#;P?`))iio&ZxB1(c1%M}WW^3yVNQWZ)n3sMy_3rdn17%JvG{=~yk z7^b0d%K!8k&!<5Q%*xz)$=t%q!rqfbn1vNw8cYtSFe`5kQ8<0$%84Uqj>sHgKi%N5 cz)O$emAGKZCnwXXKr0wLUHx3vIVCg!0EmFw6951J literal 0 HcmV?d00001 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