From 4d9cba3c11863c0e8508c526544a60f9d123c073 Mon Sep 17 00:00:00 2001 From: Stan Hebben Date: Wed, 17 Apr 2024 12:08:08 +0200 Subject: [PATCH] Initial commit --- .gitignore | 1 + Cargo.lock | 517 +++++++++ Cargo.toml | 13 + src/array_formula.rs | 1060 +++++++++++++++++++ src/bool_formula.rs | 1532 +++++++++++++++++++++++++++ src/compilation/compiler.rs | 1272 ++++++++++++++++++++++ src/compilation/lexer.rs | 172 +++ src/compilation/mod.rs | 201 ++++ src/compilation/parser.rs | 373 +++++++ src/compilation/tests.rs | 195 ++++ src/compilation/typed_formula.rs | 36 + src/date_formula.rs | 362 +++++++ src/datetime_formula.rs | 414 ++++++++ src/decimal_formula.rs | 1132 ++++++++++++++++++++ src/formula_reader.rs | 88 ++ src/formula_string.rs | 77 ++ src/formula_writer.rs | 75 ++ src/generic.rs | 539 ++++++++++ src/int_formula.rs | 1698 ++++++++++++++++++++++++++++++ src/lib.rs | 185 ++++ src/object.rs | 122 +++ src/object_formula.rs | 156 +++ src/string_formula.rs | 817 ++++++++++++++ src/string_utils.rs | 12 + src/types.rs | 511 +++++++++ src/value.rs | 792 ++++++++++++++ 26 files changed, 12352 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/array_formula.rs create mode 100644 src/bool_formula.rs create mode 100644 src/compilation/compiler.rs create mode 100644 src/compilation/lexer.rs create mode 100644 src/compilation/mod.rs create mode 100644 src/compilation/parser.rs create mode 100644 src/compilation/tests.rs create mode 100644 src/compilation/typed_formula.rs create mode 100644 src/date_formula.rs create mode 100644 src/datetime_formula.rs create mode 100644 src/decimal_formula.rs create mode 100644 src/formula_reader.rs create mode 100644 src/formula_string.rs create mode 100644 src/formula_writer.rs create mode 100644 src/generic.rs create mode 100644 src/int_formula.rs create mode 100644 src/lib.rs create mode 100644 src/object.rs create mode 100644 src/object_formula.rs create mode 100644 src/string_formula.rs create mode 100644 src/string_utils.rs create mode 100644 src/types.rs create mode 100644 src/value.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1013b26 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,517 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + +[[package]] +name = "bigdecimal" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9324c8014cd04590682b34f1e9448d38f0674d0f7b2dc553331016ef0e4e9ebc" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "chrono-tz" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "logos" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" +dependencies = [ + "beef", + "fnv", + "proc-macro2", + "quote", + "regex-syntax 0.6.29", + "syn", +] + +[[package]] +name = "logos-derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" +dependencies = [ + "logos-codegen", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parse-zoneinfo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +dependencies = [ + "regex", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "proc-macro2" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "sprocket-formulas" +version = "0.1.0" +dependencies = [ + "bigdecimal", + "chrono", + "chrono-tz", + "logos", + "regex", +] + +[[package]] +name = "syn" +version = "2.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..311d9ea --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "sprocket-formulas" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bigdecimal = "0.4.2" +chrono = "0.4.31" +chrono-tz = "0.8.5" +regex = "1.10.2" +logos = "0.13.0" diff --git a/src/array_formula.rs b/src/array_formula.rs new file mode 100644 index 0000000..42f858f --- /dev/null +++ b/src/array_formula.rs @@ -0,0 +1,1060 @@ +use regex::Regex; + +use crate::{ + formula_reader::FormulaReader, + formula_string::{FormulaString, OperatorPriority}, + generic::*, + types::*, + value::EnumValueFormula, + BoolArrayFormula, BoolFormula, BoolFormulas, DateArrayFormula, DateFormula, + DateTimeArrayFormula, DateTimeFormula, DecimalArrayFormula, DecimalFormula, + DeserializationError, DeserializedResult, ExecutionResult, Formula, IntArrayFormula, + IntFormula, ObjectArrayFormula, ObjectFormula, StringArrayFormula, StringFormula, + StringFormulas, +}; + +const GENERIC_AOP_MAP_FROM_BOOL: u8 = 0x10; +const GENERIC_AOP_MAP_FROM_INT: u8 = 0x11; +const GENERIC_AOP_MAP_FROM_DECIMAL: u8 = 0x12; +const GENERIC_AOP_MAP_FROM_STRING: u8 = 0x13; +const GENERIC_AOP_MAP_FROM_DATE: u8 = 0x14; +const GENERIC_AOP_MAP_FROM_DATETIME: u8 = 0x15; +const GENERIC_AOP_MAP_FROM_OBJECT: u8 = 0x16; +const GENERIC_AOP_FILTER: u8 = 0x17; + +pub struct BoolArrayFormulas; + +impl BoolArrayFormulas { + pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::::deserialize(reader), + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + GENERIC_AOP_MAP_FROM_BOOL => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_INT => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DECIMAL => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_STRING => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATE => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATETIME => { + ArrayMap::::deserialize(reader) + } + GENERIC_AOP_MAP_FROM_OBJECT => ArrayMap::::deserialize(reader), + GENERIC_AOP_FILTER => ArrayFilter::::deserialize(reader), + other => Err(DeserializationError::UnknownOperator( + Type::BoolArray, + other, + )), + } + } + + pub fn invalid(formula: String) -> BoolArrayFormula { + InvalidFormula::::new(formula) + } + + pub fn define_locals( + locals: Vec, + formula: BoolArrayFormula, + ) -> BoolArrayFormula { + DefineLocals::::new(locals, formula) + } + + pub fn local(index: u32) -> BoolArrayFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> Box>> { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> BoolArrayFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn ternary( + condition: BoolFormula, + true_value: BoolArrayFormula, + false_value: BoolArrayFormula, + ) -> BoolArrayFormula { + Ternary::::new(condition, true_value, false_value) + } + + pub fn map_from_bool(array: BoolArrayFormula, function: BoolFormula) -> BoolArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_int(array: IntArrayFormula, function: BoolFormula) -> BoolArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_decimal(array: DecimalArrayFormula, function: BoolFormula) -> BoolArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_string(array: StringArrayFormula, function: BoolFormula) -> BoolArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_date(array: DateArrayFormula, function: BoolFormula) -> BoolArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_datetime( + array: DateTimeArrayFormula, + function: BoolFormula, + ) -> BoolArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_object(array: ObjectArrayFormula, function: BoolFormula) -> BoolArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn filter(array: BoolArrayFormula, function: BoolFormula) -> BoolArrayFormula { + ArrayFilter::::new(array, function) + } +} + +pub struct IntArrayFormulas; + +impl IntArrayFormulas { + pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::::deserialize(reader), + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + GENERIC_AOP_MAP_FROM_BOOL => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_INT => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DECIMAL => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_STRING => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATE => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATETIME => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_OBJECT => ArrayMap::::deserialize(reader), + GENERIC_AOP_FILTER => ArrayFilter::::deserialize(reader), + other => Err(DeserializationError::UnknownOperator(Type::IntArray, other)), + } + } + + pub fn define_locals( + locals: Vec, + formula: IntArrayFormula, + ) -> IntArrayFormula { + DefineLocals::::new(locals, formula) + } + + pub fn local(index: u32) -> IntArrayFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> IntArrayFormula { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> IntArrayFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn ternary( + condition: BoolFormula, + true_value: IntArrayFormula, + false_value: IntArrayFormula, + ) -> IntArrayFormula { + Ternary::::new(condition, true_value, false_value) + } + + pub fn map_from_bool(array: BoolArrayFormula, function: IntFormula) -> IntArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_int(array: IntArrayFormula, function: IntFormula) -> IntArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_decimal(array: DecimalArrayFormula, function: IntFormula) -> IntArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_string(array: StringArrayFormula, function: IntFormula) -> IntArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_date(array: DateArrayFormula, function: IntFormula) -> IntArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_datetime(array: DateTimeArrayFormula, function: IntFormula) -> IntArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_object(array: ObjectArrayFormula, function: IntFormula) -> IntArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn filter(array: IntArrayFormula, function: BoolFormula) -> IntArrayFormula { + ArrayFilter::::new(array, function) + } +} + +pub struct DecimalArrayFormulas; + +impl DecimalArrayFormulas { + pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => { + ObjectMethodCall::::deserialize(reader) + } + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + GENERIC_AOP_MAP_FROM_BOOL => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_INT => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DECIMAL => { + ArrayMap::::deserialize(reader) + } + GENERIC_AOP_MAP_FROM_STRING => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATE => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATETIME => { + ArrayMap::::deserialize(reader) + } + GENERIC_AOP_MAP_FROM_OBJECT => ArrayMap::::deserialize(reader), + GENERIC_AOP_FILTER => ArrayFilter::::deserialize(reader), + other => Err(DeserializationError::UnknownOperator( + Type::DecimalArray, + other, + )), + } + } + + pub fn define_locals( + locals: Vec, + formula: DecimalArrayFormula, + ) -> DecimalArrayFormula { + DefineLocals::::new(locals, formula) + } + + pub fn local(index: u32) -> DecimalArrayFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> DecimalArrayFormula { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> DecimalArrayFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn ternary( + condition: BoolFormula, + true_value: DecimalArrayFormula, + false_value: DecimalArrayFormula, + ) -> DecimalArrayFormula { + Ternary::::new(condition, true_value, false_value) + } + + pub fn map_from_bool(array: BoolArrayFormula, function: DecimalFormula) -> DecimalArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_int(array: IntArrayFormula, function: DecimalFormula) -> DecimalArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_decimal( + array: DecimalArrayFormula, + function: DecimalFormula, + ) -> DecimalArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_string( + array: StringArrayFormula, + function: DecimalFormula, + ) -> DecimalArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_date(array: DateArrayFormula, function: DecimalFormula) -> DecimalArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_datetime( + array: DateTimeArrayFormula, + function: DecimalFormula, + ) -> DecimalArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_object( + array: ObjectArrayFormula, + function: DecimalFormula, + ) -> DecimalArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn filter(array: DecimalArrayFormula, function: BoolFormula) -> DecimalArrayFormula { + ArrayFilter::::new(array, function) + } +} + +const STRINGARRAY_SPLIT: u8 = 0x21; +const STRINGARRAY_SPLIT_REGEX: u8 = 0x22; +const STRINGARRAY_MATCH_REGEX: u8 = 0x23; + +pub struct StringArrayFormulas; + +impl StringArrayFormulas { + pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => { + ObjectMethodCall::::deserialize(reader) + } + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + GENERIC_AOP_MAP_FROM_BOOL => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_INT => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DECIMAL => { + ArrayMap::::deserialize(reader) + } + GENERIC_AOP_MAP_FROM_STRING => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATE => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATETIME => { + ArrayMap::::deserialize(reader) + } + GENERIC_AOP_MAP_FROM_OBJECT => ArrayMap::::deserialize(reader), + GENERIC_AOP_FILTER => ArrayFilter::::deserialize(reader), + STRINGARRAY_SPLIT => StringArraySplit::deserialize(reader), + STRINGARRAY_SPLIT_REGEX => StringArraySplitRegex::deserialize(reader), + STRINGARRAY_MATCH_REGEX => StringArrayMatchRegex::deserialize(reader), + other => Err(DeserializationError::UnknownOperator( + Type::StringArray, + other, + )), + } + } + + pub fn define_locals( + locals: Vec, + formula: StringArrayFormula, + ) -> StringArrayFormula { + DefineLocals::::new(locals, formula) + } + + pub fn local(index: u32) -> StringArrayFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> StringArrayFormula { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> StringArrayFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn ternary( + condition: BoolFormula, + true_value: StringArrayFormula, + false_value: StringArrayFormula, + ) -> StringArrayFormula { + Ternary::::new(condition, true_value, false_value) + } + + pub fn map_from_bool(array: BoolArrayFormula, function: StringFormula) -> StringArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_int(array: IntArrayFormula, function: StringFormula) -> StringArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_decimal( + array: DecimalArrayFormula, + function: StringFormula, + ) -> StringArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_string( + array: StringArrayFormula, + function: StringFormula, + ) -> StringArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_date(array: DateArrayFormula, function: StringFormula) -> StringArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_datetime( + array: DateTimeArrayFormula, + function: StringFormula, + ) -> StringArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_object( + array: ObjectArrayFormula, + function: StringFormula, + ) -> StringArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn filter(array: StringArrayFormula, function: BoolFormula) -> StringArrayFormula { + ArrayFilter::::new(array, function) + } + + pub fn split(array: StringFormula, separator: char) -> StringArrayFormula { + StringArraySplit::new(array, separator, 0) + } + + pub fn split_limited(array: StringFormula, separator: char, limit: u32) -> StringArrayFormula { + StringArraySplit::new(array, separator, limit) + } + + pub fn split_regex(array: StringFormula, regex: Regex) -> StringArrayFormula { + StringArraySplitRegex::new(array, regex) + } + + pub fn match_regex(array: StringFormula, regex: Regex) -> StringArrayFormula { + StringArrayMatchRegex::new(array, regex) + } +} + +pub struct DateArrayFormulas; + +impl DateArrayFormulas { + pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::::deserialize(reader), + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + GENERIC_AOP_MAP_FROM_BOOL => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_INT => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DECIMAL => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_STRING => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATE => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATETIME => { + ArrayMap::::deserialize(reader) + } + GENERIC_AOP_MAP_FROM_OBJECT => ArrayMap::::deserialize(reader), + GENERIC_AOP_FILTER => ArrayFilter::::deserialize(reader), + other => Err(DeserializationError::UnknownOperator( + Type::DateArray, + other, + )), + } + } + + pub fn define_locals( + locals: Vec, + formula: DateArrayFormula, + ) -> DateArrayFormula { + DefineLocals::::new(locals, formula) + } + + pub fn local(index: u32) -> DateArrayFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> DateArrayFormula { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> DateArrayFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn ternary( + condition: BoolFormula, + true_value: DateArrayFormula, + false_value: DateArrayFormula, + ) -> DateArrayFormula { + Ternary::::new(condition, true_value, false_value) + } + + pub fn map_from_bool(array: BoolArrayFormula, function: DateFormula) -> DateArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_int(array: IntArrayFormula, function: DateFormula) -> DateArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_decimal(array: DecimalArrayFormula, function: DateFormula) -> DateArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_string(array: StringArrayFormula, function: DateFormula) -> DateArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_date(array: DateArrayFormula, function: DateFormula) -> DateArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_datetime( + array: DateTimeArrayFormula, + function: DateFormula, + ) -> DateArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_object(array: ObjectArrayFormula, function: DateFormula) -> DateArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn filter(array: DateArrayFormula, function: BoolFormula) -> DateArrayFormula { + ArrayFilter::::new(array, function) + } +} + +pub struct DateTimeArrayFormulas; + +impl DateTimeArrayFormulas { + pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => { + ObjectMethodCall::::deserialize(reader) + } + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + GENERIC_AOP_MAP_FROM_BOOL => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_INT => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DECIMAL => { + ArrayMap::::deserialize(reader) + } + GENERIC_AOP_MAP_FROM_STRING => { + ArrayMap::::deserialize(reader) + } + GENERIC_AOP_MAP_FROM_DATE => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATETIME => { + ArrayMap::::deserialize(reader) + } + GENERIC_AOP_MAP_FROM_OBJECT => { + ArrayMap::::deserialize(reader) + } + GENERIC_AOP_FILTER => ArrayFilter::::deserialize(reader), + other => Err(DeserializationError::UnknownOperator( + Type::DateTimeArray, + other, + )), + } + } + + pub fn define_locals( + locals: Vec, + formula: DateTimeArrayFormula, + ) -> DateTimeArrayFormula { + DefineLocals::::new(locals, formula) + } + + pub fn local(index: u32) -> DateTimeArrayFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> DateTimeArrayFormula { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> DateTimeArrayFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn ternary( + condition: BoolFormula, + true_value: DateTimeArrayFormula, + false_value: DateTimeArrayFormula, + ) -> DateTimeArrayFormula { + Ternary::::new(condition, true_value, false_value) + } + + pub fn map_from_bool( + array: BoolArrayFormula, + function: DateTimeFormula, + ) -> DateTimeArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_int(array: IntArrayFormula, function: DateTimeFormula) -> DateTimeArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_decimal( + array: DecimalArrayFormula, + function: DateTimeFormula, + ) -> DateTimeArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_string( + array: StringArrayFormula, + function: DateTimeFormula, + ) -> DateTimeArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_date( + array: DateArrayFormula, + function: DateTimeFormula, + ) -> DateTimeArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_datetime( + array: DateTimeArrayFormula, + function: DateTimeFormula, + ) -> DateTimeArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_object( + array: ObjectArrayFormula, + function: DateTimeFormula, + ) -> DateTimeArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn filter(array: DateTimeArrayFormula, function: BoolFormula) -> DateTimeArrayFormula { + ArrayFilter::::new(array, function) + } +} + +pub struct ObjectArrayFormulas; + +impl ObjectArrayFormulas { + pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => { + ObjectMethodCall::::deserialize(reader) + } + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + GENERIC_AOP_MAP_FROM_BOOL => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_INT => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DECIMAL => { + ArrayMap::::deserialize(reader) + } + GENERIC_AOP_MAP_FROM_STRING => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATE => ArrayMap::::deserialize(reader), + GENERIC_AOP_MAP_FROM_DATETIME => { + ArrayMap::::deserialize(reader) + } + GENERIC_AOP_MAP_FROM_OBJECT => ArrayMap::::deserialize(reader), + GENERIC_AOP_FILTER => ArrayFilter::::deserialize(reader), + other => Err(DeserializationError::UnknownOperator( + Type::ObjectArray, + other, + )), + } + } + + pub fn define_locals( + locals: Vec, + formula: ObjectArrayFormula, + ) -> ObjectArrayFormula { + DefineLocals::::new(locals, formula) + } + + pub fn local(index: u32) -> ObjectArrayFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> ObjectArrayFormula { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> ObjectArrayFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn ternary( + condition: BoolFormula, + true_value: ObjectArrayFormula, + false_value: ObjectArrayFormula, + ) -> ObjectArrayFormula { + Ternary::::new(condition, true_value, false_value) + } + + pub fn map_from_bool(array: BoolArrayFormula, function: ObjectFormula) -> ObjectArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_int(array: IntArrayFormula, function: ObjectFormula) -> ObjectArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_decimal( + array: DecimalArrayFormula, + function: ObjectFormula, + ) -> ObjectArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_string( + array: StringArrayFormula, + function: ObjectFormula, + ) -> ObjectArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_date(array: DateArrayFormula, function: ObjectFormula) -> ObjectArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_datetime( + array: DateTimeArrayFormula, + function: ObjectFormula, + ) -> ObjectArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn map_from_object( + array: ObjectArrayFormula, + function: ObjectFormula, + ) -> ObjectArrayFormula { + ArrayMap::::new(array, function) + } + + pub fn filter(array: ObjectArrayFormula, function: BoolFormula) -> ObjectArrayFormula { + ArrayFilter::::new(array, function) + } +} + +struct ArrayMap { + array: Box>>, + function: Box>, +} + +impl ArrayMap { + pub fn new( + array: Box>>, + function: Box>, + ) -> Box>> { + Box::new(Self { array, function }) + } + + pub fn deserialize( + reader: &mut FormulaReader, + ) -> DeserializedResult>>> { + let array = T1::ARRAY::formula_from_reader(reader)?; + let function = T2::formula_from_reader(reader)?; + Ok(ArrayMap::::new(array, function)) + } +} + +impl Formula> for ArrayMap { + fn evaluate(&self, context: &crate::FormulaContext) -> ExecutionResult> { + let array = self.array.evaluate(context)?; + let mut local_context = context.clone(); + Ok(array + .into_iter() + .map(|value| { + local_context.locals.push(T1::cast_to_value(value)); + let result = self.function.evaluate(&context); + local_context.locals.pop(); + result + }) + .collect::, crate::ExecutionError>>()?) + } + + fn serialize_to(&self, writer: &mut crate::FormulaWriter) { + let op = match T1::TYPE { + Type::Bool => GENERIC_AOP_MAP_FROM_BOOL, + Type::Int => GENERIC_AOP_MAP_FROM_INT, + Type::Decimal => GENERIC_AOP_MAP_FROM_DECIMAL, + Type::String => GENERIC_AOP_MAP_FROM_STRING, + Type::Date => GENERIC_AOP_MAP_FROM_DATE, + Type::DateTime => GENERIC_AOP_MAP_FROM_DATETIME, + Type::Object => GENERIC_AOP_MAP_FROM_OBJECT, + _ => unreachable!(), + }; + writer.write_byte(op); + self.array.serialize_to(writer); + self.function.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let array = self + .array + .to_formula_string() + .wrap(OperatorPriority::Member); + let function = self + .function + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("{}.map(|x| {})", array, function), + OperatorPriority::Member, + ) + } +} + +struct ArrayFilter { + array: Box>>, + function: Box>, +} + +impl ArrayFilter { + pub fn new( + array: Box>>, + function: Box>, + ) -> Box>> { + Box::new(Self { array, function }) + } + + pub fn deserialize( + reader: &mut FormulaReader, + ) -> DeserializedResult>>> { + let array = T::ARRAY::formula_from_reader(reader)?; + let function = BoolFormulas::from_reader(reader)?; + Ok(ArrayFilter::::new(array, function)) + } +} + +impl Formula> for ArrayFilter { + fn evaluate(&self, context: &crate::FormulaContext) -> ExecutionResult> { + let array = self.array.evaluate(context)?; + let mut local_context = context.clone(); + Ok(array + .into_iter() + .filter_map(|value| { + local_context.locals.push(T::cast_to_value(value.clone())); + let result = self + .function + .evaluate(&context) + .map(|v| if v { Some(value) } else { None }) + .transpose(); + local_context.locals.pop(); + result + }) + .collect::>>()?) + } + + fn serialize_to(&self, writer: &mut crate::FormulaWriter) { + writer.write_byte(GENERIC_AOP_FILTER); + self.array.serialize_to(writer); + self.function.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let array = self + .array + .to_formula_string() + .wrap(OperatorPriority::Member); + let function = self + .function + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("{}.filter(|x| {})", array, function), + OperatorPriority::Member, + ) + } +} + +struct StringArraySplit { + string: StringFormula, + separator: char, + limit: u32, +} + +impl StringArraySplit { + pub fn new(string: StringFormula, separator: char, limit: u32) -> StringArrayFormula { + Box::new(Self { + string, + separator, + limit, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let string = StringFormulas::from_reader(reader)?; + let separator = reader.read_char()?; + let limit = reader.read_u32()?; + Ok(Self::new(string, separator, limit)) + } +} + +impl Formula> for StringArraySplit { + fn evaluate(&self, context: &crate::FormulaContext) -> ExecutionResult> { + let string = self.string.evaluate(context)?; + if self.limit == 0 { + Ok(string.split(self.separator).map(|s| s.to_owned()).collect()) + } else { + Ok(string + .splitn(self.limit as usize, self.separator) + .map(|s| s.to_owned()) + .collect()) + } + } + + fn serialize_to(&self, writer: &mut crate::FormulaWriter) { + writer.write_byte(STRINGARRAY_SPLIT); + self.string.serialize_to(writer); + writer.write_char(self.separator); + writer.write_u32(self.limit); + } + + fn to_formula_string(&self) -> FormulaString { + let string = self + .string + .to_formula_string() + .wrap(OperatorPriority::Member); + FormulaString::new( + format!("{}.split({:?}, {})", string, self.separator, self.limit), + OperatorPriority::Member, + ) + } +} + +struct StringArraySplitRegex { + string: StringFormula, + regex: regex::Regex, +} + +impl StringArraySplitRegex { + pub fn new(string: StringFormula, regex: regex::Regex) -> StringArrayFormula { + Box::new(Self { string, regex }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let string = StringFormulas::from_reader(reader)?; + let regex = reader.read_string()?; + let regex = + regex::Regex::new(®ex).map_err(|_| DeserializationError::InvalidRegex(regex))?; + Ok(Self::new(string, regex)) + } +} + +impl Formula> for StringArraySplitRegex { + fn evaluate(&self, context: &crate::FormulaContext) -> ExecutionResult> { + let string = self.string.evaluate(context)?; + Ok(self.regex.split(&string).map(|s| s.to_owned()).collect()) + } + + fn serialize_to(&self, writer: &mut crate::FormulaWriter) { + writer.write_byte(STRINGARRAY_SPLIT_REGEX); + self.string.serialize_to(writer); + writer.write_string(&self.regex.as_str()); + } + + fn to_formula_string(&self) -> FormulaString { + let string = self + .string + .to_formula_string() + .wrap(OperatorPriority::Member); + FormulaString::new( + format!("{}.split_regex({:?})", string, self.regex.as_str()), + OperatorPriority::Member, + ) + } +} + +struct StringArrayMatchRegex { + string: StringFormula, + regex: regex::Regex, +} + +impl StringArrayMatchRegex { + pub fn new(string: StringFormula, regex: regex::Regex) -> StringArrayFormula { + Box::new(Self { string, regex }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let string = StringFormulas::from_reader(reader)?; + let regex = reader.read_string()?; + let regex = + regex::Regex::new(®ex).map_err(|_| DeserializationError::InvalidRegex(regex))?; + Ok(Self::new(string, regex)) + } +} + +impl Formula> for StringArrayMatchRegex { + fn evaluate(&self, context: &crate::FormulaContext) -> ExecutionResult> { + let string = self.string.evaluate(context)?; + let captures = self.regex.captures(&string); + if let Some(captures) = captures { + Ok(captures + .iter() + .map(|capture| capture.unwrap().as_str().to_owned()) + .collect()) + } else { + Ok(Vec::new()) + } + } + + fn serialize_to(&self, writer: &mut crate::FormulaWriter) { + writer.write_byte(STRINGARRAY_MATCH_REGEX); + self.string.serialize_to(writer); + writer.write_string(&self.regex.as_str()); + } + + fn to_formula_string(&self) -> FormulaString { + let string = self + .string + .to_formula_string() + .wrap(OperatorPriority::Member); + FormulaString::new( + format!("{}.match_regex({:?})", string, self.regex.as_str()), + OperatorPriority::Member, + ) + } +} + +#[cfg(test)] +mod tests {} diff --git a/src/bool_formula.rs b/src/bool_formula.rs new file mode 100644 index 0000000..2bb5c7b --- /dev/null +++ b/src/bool_formula.rs @@ -0,0 +1,1532 @@ +use bigdecimal::BigDecimal; +use regex::Regex; + +use crate::formula_reader::FormulaReader; +use crate::formula_string::FormulaString; +use crate::formula_string::OperatorPriority; +use crate::formula_writer::FormulaWriter; +use crate::generic::*; +use crate::types::*; +use crate::value::EnumValueFormula; +use crate::{ + BoolArrayFormula, BoolFormula, DateArrayFormula, DateFormula, DateFormulas, + DateTimeArrayFormula, DateTimeFormula, DateTimeFormulas, DecimalArrayFormula, DecimalFormula, + DecimalFormulas, DeserializationError, DeserializedResult, ExecutionResult, Formula, + FormulaContext, IntArrayFormula, IntFormula, IntFormulas, ObjectArrayFormula, ObjectFormula, + StringArrayFormula, StringFormula, StringFormulas, +}; + +const BOOL_OP_FALSE: u8 = 0x10; +const BOOL_OP_TRUE: u8 = 0x11; +const BOOL_OP_NOT: u8 = 0x12; +const BOOL_OP_AND: u8 = 0x13; +const BOOL_OP_OR: u8 = 0x14; +const BOOL_OP_XOR: u8 = 0x15; +const BOOL_OP_COMPARE_INT: u8 = 0x16; +const BOOL_OP_COMPARE_DECIMAL: u8 = 0x17; +const BOOL_OP_COMPARE_STRING: u8 = 0x18; +const BOOL_OP_COMPARE_DATE: u8 = 0x19; +const BOOL_OP_COMPARE_DATETIME: u8 = 0x1A; +const BOOL_OP_MATCH_STRING_REGEX: u8 = 0x1B; +const BOOL_EMPTY_BOOLARRAY: u8 = 0x1C; +const BOOL_EMPTY_INTARRAY: u8 = 0x1D; +const BOOL_EMPTY_DECIMALARRAY: u8 = 0x1E; +const BOOL_EMPTY_STRINGARRAY: u8 = 0x1F; +const BOOL_EMPTY_DATEARRAY: u8 = 0x20; +const BOOL_EMPTY_DATETIMEARRAY: u8 = 0x21; +const BOOL_EMPTY_OBJECTARRAY: u8 = 0x22; + +pub struct BoolFormulas {} + +impl BoolFormulas { + pub fn from_bytes(bytes: &[u8]) -> Result { + let mut reader = FormulaReader::new(bytes); + Self::from_reader(&mut reader) + } + + pub fn from_reader(reader: &mut FormulaReader) -> Result { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT => ArrayElement::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT => { + ArrayElementOrDefault::::deserialize(reader) + } + GENERIC_OP_ARRAY_FIRST => ArrayFirst::::deserialize(reader), + GENERIC_OP_ARRAY_LAST => ArrayLast::::deserialize(reader), + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + BOOL_OP_FALSE => Ok(Box::new(BoolFalse {})), + BOOL_OP_TRUE => Ok(Box::new(BoolTrue {})), + BOOL_OP_NOT => BoolNot::deserialize(reader), + BOOL_OP_AND => BoolAnd::deserialize(reader), + BOOL_OP_OR => BoolOr::deserialize(reader), + BOOL_OP_XOR => BoolXor::deserialize(reader), + BOOL_OP_COMPARE_INT => BoolCompareInt::deserialize(reader), + BOOL_OP_COMPARE_DECIMAL => BoolCompareDecimal::deserialize(reader), + BOOL_OP_COMPARE_DATE => BoolCompareDate::deserialize(reader), + BOOL_OP_COMPARE_DATETIME => BoolCompareDateTime::deserialize(reader), + BOOL_OP_COMPARE_STRING => BoolCompareString::deserialize(reader), + BOOL_OP_MATCH_STRING_REGEX => BoolMatchStringRegex::deserialize(reader), + BOOL_EMPTY_BOOLARRAY => EmptyArray::::deserialize(reader), + BOOL_EMPTY_INTARRAY => EmptyArray::::deserialize(reader), + BOOL_EMPTY_DECIMALARRAY => EmptyArray::::deserialize(reader), + BOOL_EMPTY_STRINGARRAY => EmptyArray::::deserialize(reader), + BOOL_EMPTY_DATEARRAY => EmptyArray::::deserialize(reader), + BOOL_EMPTY_DATETIMEARRAY => EmptyArray::::deserialize(reader), + BOOL_EMPTY_OBJECTARRAY => EmptyArray::::deserialize(reader), + other => Err(DeserializationError::UnknownOperator(Type::Bool, other)), + } + } + + pub fn invalid(formula: String) -> BoolFormula { + InvalidFormula::::new(formula) + } + + pub fn define_locals(locals: Vec, formula: BoolFormula) -> BoolFormula { + DefineLocals::::new(locals, formula) + } + + pub fn local(index: u32) -> BoolFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> BoolFormula { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> BoolFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn array_element(array: BoolArrayFormula, index: IntFormula) -> BoolFormula { + ArrayElement::::new(array, index) + } + + pub fn array_element_or_default( + array: BoolArrayFormula, + index: IntFormula, + default: BoolFormula, + ) -> BoolFormula { + ArrayElementOrDefault::::new(array, index, default) + } + + pub fn array_first(array: BoolArrayFormula) -> BoolFormula { + ArrayFirst::::new(array) + } + + pub fn array_last(array: BoolArrayFormula) -> BoolFormula { + ArrayLast::::new(array) + } + + pub fn ternary( + condition: BoolFormula, + true_value: BoolFormula, + false_value: BoolFormula, + ) -> BoolFormula { + Ternary::::new(condition, true_value, false_value) + } + + pub fn constant(value: bool) -> BoolFormula { + if value { + BoolTrue::new() + } else { + BoolFalse::new() + } + } + + pub fn not(operand: BoolFormula) -> BoolFormula { + BoolNot::new(operand) + } + + pub fn and(left: BoolFormula, right: BoolFormula) -> BoolFormula { + BoolAnd::new(left, right) + } + + pub fn or(left: BoolFormula, right: BoolFormula) -> BoolFormula { + BoolOr::new(left, right) + } + + pub fn xor(left: BoolFormula, right: BoolFormula) -> BoolFormula { + BoolXor::new(left, right) + } + + pub fn compare_int(left: IntFormula, right: IntFormula, comparator: Comparator) -> BoolFormula { + BoolCompareInt::new(left, right, comparator) + } + + pub fn compare_decimal( + left: DecimalFormula, + right: DecimalFormula, + comparator: Comparator, + ) -> BoolFormula { + BoolCompareDecimal::new(left, right, comparator) + } + + pub fn compare_string( + left: StringFormula, + right: StringFormula, + comparator: Comparator, + ) -> BoolFormula { + BoolCompareString::new(left, right, comparator) + } + + pub fn compare_date( + left: DateFormula, + right: DateFormula, + comparator: Comparator, + ) -> BoolFormula { + BoolCompareDate::new(left, right, comparator) + } + + pub fn compare_datetime( + left: DateTimeFormula, + right: DateTimeFormula, + comparator: Comparator, + ) -> BoolFormula { + BoolCompareDateTime::new(left, right, comparator) + } + + pub fn match_string_regex(string: StringFormula, regex: Regex) -> BoolFormula { + BoolMatchStringRegex::new(string, regex) + } + + pub fn array_empty_bool(array: BoolArrayFormula) -> BoolFormula { + EmptyArray::::new(array) + } + + pub fn array_empty_int(array: IntArrayFormula) -> BoolFormula { + EmptyArray::::new(array) + } + + pub fn array_empty_decimal(array: DecimalArrayFormula) -> BoolFormula { + EmptyArray::::new(array) + } + + pub fn array_empty_string(array: StringArrayFormula) -> BoolFormula { + EmptyArray::::new(array) + } + + pub fn array_empty_date(array: DateArrayFormula) -> BoolFormula { + EmptyArray::::new(array) + } + + pub fn array_empty_datetime(array: DateTimeArrayFormula) -> BoolFormula { + EmptyArray::::new(array) + } + + pub fn array_empty_object(array: ObjectArrayFormula) -> BoolFormula { + EmptyArray::::new(array) + } +} + +#[derive(Debug)] +pub enum Comparator { + Equal, + NotEqual, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual, +} + +impl Comparator { + pub fn deserialize(value: u8) -> Result { + match value { + 0 => Ok(Comparator::Equal), + 1 => Ok(Comparator::NotEqual), + 2 => Ok(Comparator::LessThan), + 3 => Ok(Comparator::LessThanOrEqual), + 4 => Ok(Comparator::GreaterThan), + 5 => Ok(Comparator::GreaterThanOrEqual), + other => Err(DeserializationError::UnknownComparator(other)), + } + } + + pub fn serialize(&self) -> u8 { + match self { + Comparator::Equal => 0, + Comparator::NotEqual => 1, + Comparator::LessThan => 2, + Comparator::LessThanOrEqual => 3, + Comparator::GreaterThan => 4, + Comparator::GreaterThanOrEqual => 5, + } + } + + pub fn to_string(&self) -> &'static str { + match self { + Comparator::Equal => "=", + Comparator::NotEqual => "!=", + Comparator::LessThan => "<", + Comparator::LessThanOrEqual => "<=", + Comparator::GreaterThan => ">", + Comparator::GreaterThanOrEqual => ">=", + } + } +} + +struct BoolFalse {} + +impl BoolFalse { + pub fn new() -> BoolFormula { + Box::new(Self {}) + } +} + +impl Formula for BoolFalse { + fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult { + Ok(false) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(BOOL_OP_FALSE); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new("false".to_string(), OperatorPriority::Literal) + } +} + +struct BoolTrue {} + +impl BoolTrue { + pub fn new() -> BoolFormula { + Box::new(Self {}) + } +} + +impl Formula for BoolTrue { + fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult { + Ok(true) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(BOOL_OP_TRUE); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new("true".to_string(), OperatorPriority::Literal) + } +} + +struct BoolNot { + operand: BoolFormula, +} + +impl BoolNot { + pub fn new(operand: BoolFormula) -> BoolFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = BoolFormulas::from_reader(reader)?; + Ok(Box::new(Self { operand })) + } +} + +impl Formula for BoolNot { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + Ok(!operand) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(BOOL_OP_NOT); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.operand.as_ref(), OperatorPriority::Unary, |operand| { + format!("!{}", operand) + }) + } +} + +struct BoolOr { + left: BoolFormula, + right: BoolFormula, +} + +impl BoolOr { + pub fn new(left: BoolFormula, right: BoolFormula) -> BoolFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = BoolFormulas::from_reader(reader)?; + let right = BoolFormulas::from_reader(reader)?; + Ok(Box::new(Self { left, right })) + } +} + +impl Formula for BoolOr { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left || right) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(BOOL_OP_OR); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Or, + |left, right| format!("{} || {}", left, right), + ) + } +} + +struct BoolAnd { + left: BoolFormula, + right: BoolFormula, +} + +impl BoolAnd { + pub fn new(left: BoolFormula, right: BoolFormula) -> BoolFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = BoolFormulas::from_reader(reader)?; + let right = BoolFormulas::from_reader(reader)?; + Ok(Box::new(Self { left, right })) + } +} + +impl Formula for BoolAnd { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left && right) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(BOOL_OP_AND); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::And, + |left, right| format!("{} && {}", left, right), + ) + } +} + +struct BoolXor { + left: BoolFormula, + right: BoolFormula, +} + +impl BoolXor { + pub fn new(left: BoolFormula, right: BoolFormula) -> BoolFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = BoolFormulas::from_reader(reader)?; + let right = BoolFormulas::from_reader(reader)?; + Ok(Box::new(Self { left, right })) + } +} + +impl Formula for BoolXor { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left ^ right) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(BOOL_OP_XOR); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let left = self.left.to_formula_string(); + let right = self.right.to_formula_string(); + + FormulaString::new( + format!( + "{} ^ {}", + left.wrap(OperatorPriority::Or), + right.wrap(OperatorPriority::Or) + ), + OperatorPriority::Or, + ) + } +} + +struct BoolCompareInt { + left: IntFormula, + right: IntFormula, + comparator: Comparator, +} + +impl BoolCompareInt { + pub fn new(left: IntFormula, right: IntFormula, comparator: Comparator) -> BoolFormula { + Box::new(Self { + left, + right, + comparator, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let comparator = Comparator::deserialize(reader.read_byte()?)?; + let left = IntFormulas::from_reader(reader)?; + let right = IntFormulas::from_reader(reader)?; + Ok(Box::new(Self { + left, + right, + comparator, + })) + } +} + +impl Formula for BoolCompareInt { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + + match self.comparator { + Comparator::Equal => Ok(left == right), + Comparator::NotEqual => Ok(left != right), + Comparator::LessThan => Ok(left < right), + Comparator::LessThanOrEqual => Ok(left <= right), + Comparator::GreaterThan => Ok(left > right), + Comparator::GreaterThanOrEqual => Ok(left >= right), + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(BOOL_OP_COMPARE_INT); + writer.write_byte(self.comparator.serialize()); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let left = self.left.to_formula_string(); + let right = self.right.to_formula_string(); + + FormulaString::new( + format!( + "{} {} {}", + left.wrap(OperatorPriority::Comparison), + self.comparator.to_string(), + right.wrap(OperatorPriority::Comparison) + ), + OperatorPriority::Comparison, + ) + } +} + +struct BoolCompareDecimal { + left: Box>, + right: Box>, + comparator: Comparator, +} + +impl BoolCompareDecimal { + pub fn new( + left: Box>, + right: Box>, + comparator: Comparator, + ) -> BoolFormula { + Box::new(Self { + left, + right, + comparator, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let comparator = Comparator::deserialize(reader.read_byte()?)?; + let left = DecimalFormulas::from_reader(reader)?; + let right = DecimalFormulas::from_reader(reader)?; + Ok(Box::new(Self { + left, + right, + comparator, + })) + } +} + +impl Formula for BoolCompareDecimal { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + + match self.comparator { + Comparator::Equal => Ok(left == right), + Comparator::NotEqual => Ok(left != right), + Comparator::LessThan => Ok(left < right), + Comparator::LessThanOrEqual => Ok(left <= right), + Comparator::GreaterThan => Ok(left > right), + Comparator::GreaterThanOrEqual => Ok(left >= right), + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(BOOL_OP_COMPARE_DECIMAL); + writer.write_byte(self.comparator.serialize()); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let left = self.left.to_formula_string(); + let right = self.right.to_formula_string(); + + FormulaString::new( + format!( + "{} {} {}", + left.wrap(OperatorPriority::Comparison), + self.comparator.to_string(), + right.wrap(OperatorPriority::Comparison) + ), + OperatorPriority::Comparison, + ) + } +} + +struct BoolCompareString { + left: StringFormula, + right: StringFormula, + comparator: Comparator, +} + +impl BoolCompareString { + pub fn new(left: StringFormula, right: StringFormula, comparator: Comparator) -> BoolFormula { + Box::new(Self { + left, + right, + comparator, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let comparator = Comparator::deserialize(reader.read_byte()?)?; + let left = StringFormulas::from_reader(reader)?; + let right = StringFormulas::from_reader(reader)?; + Ok(Box::new(Self { + left, + right, + comparator, + })) + } +} + +impl Formula for BoolCompareString { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + + match self.comparator { + Comparator::Equal => Ok(left == right), + Comparator::NotEqual => Ok(left != right), + Comparator::LessThan => Ok(left < right), + Comparator::LessThanOrEqual => Ok(left <= right), + Comparator::GreaterThan => Ok(left > right), + Comparator::GreaterThanOrEqual => Ok(left >= right), + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(BOOL_OP_COMPARE_STRING); + writer.write_byte(self.comparator.serialize()); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let left = self.left.to_formula_string(); + let right = self.right.to_formula_string(); + + FormulaString::new( + format!( + "{} {} {}", + left.wrap(OperatorPriority::Comparison), + self.comparator.to_string(), + right.wrap(OperatorPriority::Comparison) + ), + OperatorPriority::Comparison, + ) + } +} + +struct BoolCompareDate { + left: DateFormula, + right: DateFormula, + comparator: Comparator, +} + +impl BoolCompareDate { + pub fn new(left: DateFormula, right: DateFormula, comparator: Comparator) -> BoolFormula { + Box::new(Self { + left, + right, + comparator, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let comparator = Comparator::deserialize(reader.read_byte()?)?; + let left = DateFormulas::from_reader(reader)?; + let right = DateFormulas::from_reader(reader)?; + Ok(Box::new(Self { + left, + right, + comparator, + })) + } +} + +impl Formula for BoolCompareDate { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + + match self.comparator { + Comparator::Equal => Ok(left == right), + Comparator::NotEqual => Ok(left != right), + Comparator::LessThan => Ok(left < right), + Comparator::LessThanOrEqual => Ok(left <= right), + Comparator::GreaterThan => Ok(left > right), + Comparator::GreaterThanOrEqual => Ok(left >= right), + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(BOOL_OP_COMPARE_DATE); + writer.write_byte(self.comparator.serialize()); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let left = self.left.to_formula_string(); + let right = self.right.to_formula_string(); + + FormulaString::new( + format!( + "{} {} {}", + left.wrap(OperatorPriority::Comparison), + self.comparator.to_string(), + right.wrap(OperatorPriority::Comparison) + ), + OperatorPriority::Comparison, + ) + } +} + +struct BoolCompareDateTime { + left: DateTimeFormula, + right: DateTimeFormula, + comparator: Comparator, +} + +impl BoolCompareDateTime { + pub fn new( + left: DateTimeFormula, + right: DateTimeFormula, + comparator: Comparator, + ) -> BoolFormula { + Box::new(Self { + left, + right, + comparator, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let comparator = Comparator::deserialize(reader.read_byte()?)?; + let left = DateTimeFormulas::from_reader(reader)?; + let right = DateTimeFormulas::from_reader(reader)?; + Ok(Box::new(Self { + left, + right, + comparator, + })) + } +} + +impl Formula for BoolCompareDateTime { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + + match self.comparator { + Comparator::Equal => Ok(left == right), + Comparator::NotEqual => Ok(left != right), + Comparator::LessThan => Ok(left < right), + Comparator::LessThanOrEqual => Ok(left <= right), + Comparator::GreaterThan => Ok(left > right), + Comparator::GreaterThanOrEqual => Ok(left >= right), + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(BOOL_OP_COMPARE_DATETIME); + writer.write_byte(self.comparator.serialize()); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let left = self.left.to_formula_string(); + let right = self.right.to_formula_string(); + + FormulaString::new( + format!( + "{} {} {}", + left.wrap(OperatorPriority::Comparison), + self.comparator.to_string(), + right.wrap(OperatorPriority::Comparison) + ), + OperatorPriority::Comparison, + ) + } +} + +struct BoolMatchStringRegex { + string: StringFormula, + regex: Regex, +} + +impl BoolMatchStringRegex { + pub fn new(string: StringFormula, regex: Regex) -> BoolFormula { + Box::new(Self { string, regex }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let string = StringFormulas::from_reader(reader)?; + let regex = reader.read_string()?; + let regex = Regex::new(®ex).map_err(|_| DeserializationError::InvalidRegex(regex))?; + Ok(Box::new(Self { string, regex })) + } +} + +impl Formula for BoolMatchStringRegex { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let string = self.string.evaluate(context)?; + Ok(self.regex.is_match(&string)) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(BOOL_OP_MATCH_STRING_REGEX); + self.string.serialize_to(writer); + writer.write_string(&self.regex.as_str()); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new( + format!( + "{}.match({})", + self.string.to_string(), + self.regex.as_str().replace("/", r"\/") + ), + OperatorPriority::Comparison, + ) + } +} + +struct EmptyArray { + array: Box>>, +} + +impl EmptyArray { + pub fn new(array: Box>>) -> BoolFormula { + Box::new(Self { array }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let array = T::ARRAY::formula_from_reader(reader)?; + Ok(Self::new(array)) + } +} + +impl Formula for EmptyArray { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let array = self.array.evaluate(context)?; + Ok(array.is_empty()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + let op = match T::TYPE { + Type::Bool => BOOL_EMPTY_BOOLARRAY, + Type::Int => BOOL_EMPTY_INTARRAY, + Type::Decimal => BOOL_EMPTY_DECIMALARRAY, + Type::String => BOOL_EMPTY_STRINGARRAY, + Type::Date => BOOL_EMPTY_DATEARRAY, + Type::DateTime => BOOL_EMPTY_DATETIMEARRAY, + Type::Object => BOOL_EMPTY_OBJECTARRAY, + _ => unreachable!(), + }; + writer.write_byte(op); + self.array.serialize_to(writer); + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + FormulaString::unary(self.array.as_ref(), OperatorPriority::Member, |array| { + format!("{}.empty", array) + }) + } +} + +#[cfg(test)] +mod tests { + use crate::{bool_formula::Comparator, object::TestObject, value::Value, ObjectFormulas}; + + #[test] + fn test_local() { + let formula = crate::BoolFormulas::local(0); + let mut context = crate::FormulaContext::new_with_empty_root(); + context.locals.push(Value::Bool(true)); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } + + #[test] + fn test_object_field() { + let formula = crate::BoolFormulas::object_field( + crate::ObjectFormulas::root(), + "bool_true".to_string(), + ); + let context = crate::FormulaContext::new(TestObject::new(1)); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } + + #[test] + fn test_object_call() { + let formula = crate::BoolFormulas::object_method_call( + crate::ObjectFormulas::root(), + "really".to_string(), + vec![], + ); + let context = crate::FormulaContext::new(TestObject::new(1)); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } + + #[test] + fn test_false() { + let formula = crate::BoolFormulas::constant(false); + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, false); + } + + #[test] + fn test_true() { + let formula = crate::BoolFormulas::constant(true); + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } + + #[test] + fn test_not() { + let formula = crate::BoolFormulas::not(crate::BoolFormulas::constant(true)); + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, false); + } + + #[test] + fn test_and() { + let formula = crate::BoolFormulas::and( + crate::BoolFormulas::constant(true), + crate::BoolFormulas::constant(false), + ); + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::and( + crate::BoolFormulas::constant(true), + crate::BoolFormulas::constant(true), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::and( + crate::BoolFormulas::constant(false), + crate::BoolFormulas::constant(false), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, false); + } + + #[test] + fn test_or() { + let formula = crate::BoolFormulas::or( + crate::BoolFormulas::constant(true), + crate::BoolFormulas::constant(false), + ); + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::or( + crate::BoolFormulas::constant(true), + crate::BoolFormulas::constant(true), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::or( + crate::BoolFormulas::constant(false), + crate::BoolFormulas::constant(false), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, false); + } + + #[test] + fn test_xor() { + let formula = crate::BoolFormulas::xor( + crate::BoolFormulas::constant(true), + crate::BoolFormulas::constant(false), + ); + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::xor( + crate::BoolFormulas::constant(true), + crate::BoolFormulas::constant(true), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::xor( + crate::BoolFormulas::constant(false), + crate::BoolFormulas::constant(false), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::xor( + crate::BoolFormulas::constant(false), + crate::BoolFormulas::constant(true), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } + + #[test] + fn test_compare_int() { + let formula = crate::BoolFormulas::compare_int( + crate::IntFormulas::value(1), + crate::IntFormulas::value(2), + Comparator::Equal, + ); + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::compare_int( + crate::IntFormulas::value(1), + crate::IntFormulas::value(2), + Comparator::NotEqual, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::compare_int( + crate::IntFormulas::value(1), + crate::IntFormulas::value(2), + Comparator::LessThan, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::compare_int( + crate::IntFormulas::value(1), + crate::IntFormulas::value(2), + Comparator::LessThanOrEqual, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::compare_int( + crate::IntFormulas::value(1), + crate::IntFormulas::value(2), + Comparator::GreaterThan, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::compare_int( + crate::IntFormulas::value(1), + crate::IntFormulas::value(2), + Comparator::GreaterThanOrEqual, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::compare_int( + crate::IntFormulas::value(2), + crate::IntFormulas::value(2), + Comparator::Equal, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } + + #[test] + fn test_compare_decimal() { + let formula = crate::BoolFormulas::compare_decimal( + crate::DecimalFormulas::value("1.0".parse().unwrap()), + crate::DecimalFormulas::value("2.0".parse().unwrap()), + Comparator::Equal, + ); + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::compare_decimal( + crate::DecimalFormulas::value("1.0".parse().unwrap()), + crate::DecimalFormulas::value("2.0".parse().unwrap()), + Comparator::NotEqual, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::compare_decimal( + crate::DecimalFormulas::value("1.0".parse().unwrap()), + crate::DecimalFormulas::value("2.0".parse().unwrap()), + Comparator::LessThan, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::compare_decimal( + crate::DecimalFormulas::value("1.0".parse().unwrap()), + crate::DecimalFormulas::value("2.0".parse().unwrap()), + Comparator::LessThanOrEqual, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::compare_decimal( + crate::DecimalFormulas::value("1.0".parse().unwrap()), + crate::DecimalFormulas::value("2.0".parse().unwrap()), + Comparator::GreaterThan, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::compare_decimal( + crate::DecimalFormulas::value("1.0".parse().unwrap()), + crate::DecimalFormulas::value("2.0".parse().unwrap()), + Comparator::GreaterThanOrEqual, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::compare_decimal( + crate::DecimalFormulas::value("1.0".parse().unwrap()), + crate::DecimalFormulas::value("2.0".parse().unwrap()), + Comparator::Equal, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } + + #[test] + fn test_compare_date() { + let formula = crate::BoolFormulas::compare_date( + crate::DateFormulas::value("2020-01-01".parse().unwrap()), + crate::DateFormulas::value("2020-01-02".parse().unwrap()), + Comparator::Equal, + ); + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::compare_date( + crate::DateFormulas::value("2020-01-01".parse().unwrap()), + crate::DateFormulas::value("2020-01-02".parse().unwrap()), + Comparator::NotEqual, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::compare_date( + crate::DateFormulas::value("2020-01-01".parse().unwrap()), + crate::DateFormulas::value("2020-01-02".parse().unwrap()), + Comparator::LessThan, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::compare_date( + crate::DateFormulas::value("2020-01-01".parse().unwrap()), + crate::DateFormulas::value("2020-01-02".parse().unwrap()), + Comparator::LessThanOrEqual, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::compare_date( + crate::DateFormulas::value("2020-01-01".parse().unwrap()), + crate::DateFormulas::value("2020-01-02".parse().unwrap()), + Comparator::GreaterThan, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::compare_date( + crate::DateFormulas::value("2020-01-01".parse().unwrap()), + crate::DateFormulas::value("2020-01-02".parse().unwrap()), + Comparator::GreaterThanOrEqual, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, false); + } + + #[test] + fn test_compare_datetime() { + let formula = crate::BoolFormulas::compare_datetime( + crate::DateTimeFormulas::value("2020-01-01T00:00:00Z".parse().unwrap()), + crate::DateTimeFormulas::value("2020-01-01T00:00:01Z".parse().unwrap()), + Comparator::Equal, + ); + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::compare_datetime( + crate::DateTimeFormulas::value("2020-01-01T00:00:00Z".parse().unwrap()), + crate::DateTimeFormulas::value("2020-01-01T00:00:01Z".parse().unwrap()), + Comparator::NotEqual, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::compare_datetime( + crate::DateTimeFormulas::value("2020-01-01T00:00:00Z".parse().unwrap()), + crate::DateTimeFormulas::value("2020-01-01T00:00:01Z".parse().unwrap()), + Comparator::LessThan, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::compare_datetime( + crate::DateTimeFormulas::value("2020-01-01T00:00:00Z".parse().unwrap()), + crate::DateTimeFormulas::value("2020-01-01T00:00:01Z".parse().unwrap()), + Comparator::LessThanOrEqual, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::compare_datetime( + crate::DateTimeFormulas::value("2020-01-01T00:00:00Z".parse().unwrap()), + crate::DateTimeFormulas::value("2020-01-01T00:00:01Z".parse().unwrap()), + Comparator::GreaterThan, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::compare_datetime( + crate::DateTimeFormulas::value("2020-01-01T00:00:00Z".parse().unwrap()), + crate::DateTimeFormulas::value("2020-01-01T00:00:01Z".parse().unwrap()), + Comparator::GreaterThanOrEqual, + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, false); + } + + #[test] + fn test_match_string_regex() { + let formula = crate::BoolFormulas::match_string_regex( + crate::StringFormulas::value("abc".to_string()), + regex::Regex::new("abc").unwrap(), + ); + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::match_string_regex( + crate::StringFormulas::value("abc".to_string()), + regex::Regex::new("def").unwrap(), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::match_string_regex( + crate::StringFormulas::value("abc".to_string()), + regex::Regex::new("a.c").unwrap(), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::match_string_regex( + crate::StringFormulas::value("abc".to_string()), + regex::Regex::new("a.d").unwrap(), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::match_string_regex( + crate::StringFormulas::value("abc".to_string()), + regex::Regex::new("a.*c").unwrap(), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let formula = crate::BoolFormulas::match_string_regex( + crate::StringFormulas::value("abc".to_string()), + regex::Regex::new("a.*d").unwrap(), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::match_string_regex( + crate::StringFormulas::value("abc".to_string()), + regex::Regex::new("a.*").unwrap(), + ); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } + + #[test] + fn test_bool_array_empty() { + let formula = + crate::BoolFormulas::array_empty_bool(crate::BoolArrayFormulas::object_field( + ObjectFormulas::root(), + "bool_array".to_string(), + )); + let context = crate::FormulaContext::new(TestObject::new(1)); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = + crate::BoolFormulas::array_empty_bool(crate::BoolArrayFormulas::object_field( + ObjectFormulas::root(), + "empty_bool_array".to_string(), + )); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } + + #[test] + fn test_int_array_empty() { + let formula = crate::BoolFormulas::array_empty_int(crate::IntArrayFormulas::object_field( + ObjectFormulas::root(), + "int_array".to_string(), + )); + let context = crate::FormulaContext::new(TestObject::new(1)); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = crate::BoolFormulas::array_empty_int(crate::IntArrayFormulas::object_field( + ObjectFormulas::root(), + "empty_int_array".to_string(), + )); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, false); + } + + #[test] + fn test_decimal_array_empty() { + let formula = + crate::BoolFormulas::array_empty_decimal(crate::DecimalArrayFormulas::object_field( + ObjectFormulas::root(), + "decimal_array".to_string(), + )); + let context = crate::FormulaContext::new(TestObject::new(1)); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = + crate::BoolFormulas::array_empty_decimal(crate::DecimalArrayFormulas::object_field( + ObjectFormulas::root(), + "empty_decimal_array".to_string(), + )); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } + + #[test] + fn test_string_array_empty() { + let formula = + crate::BoolFormulas::array_empty_string(crate::StringArrayFormulas::object_field( + ObjectFormulas::root(), + "string_array".to_string(), + )); + let context = crate::FormulaContext::new(TestObject::new(1)); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = + crate::BoolFormulas::array_empty_string(crate::StringArrayFormulas::object_field( + ObjectFormulas::root(), + "empty_string_array".to_string(), + )); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } + + #[test] + fn test_date_array_empty() { + let formula = + crate::BoolFormulas::array_empty_date(crate::DateArrayFormulas::object_field( + ObjectFormulas::root(), + "date_array".to_string(), + )); + let context = crate::FormulaContext::new(TestObject::new(1)); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = + crate::BoolFormulas::array_empty_date(crate::DateArrayFormulas::object_field( + ObjectFormulas::root(), + "empty_date_array".to_string(), + )); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } + + #[test] + fn test_datetime_array_empty() { + let formula = + crate::BoolFormulas::array_empty_datetime(crate::DateTimeArrayFormulas::object_field( + ObjectFormulas::root(), + "datetime_array".to_string(), + )); + let context = crate::FormulaContext::new(TestObject::new(1)); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, false); + + let formula = + crate::BoolFormulas::array_empty_datetime(crate::DateTimeArrayFormulas::object_field( + ObjectFormulas::root(), + "empty_datetime_array".to_string(), + )); + let result = formula.evaluate(&context).unwrap(); + assert_eq!(result, true); + + let serialized = formula.serialize(); + let deserialized = crate::BoolFormulas::from_bytes(&serialized).unwrap(); + let result = deserialized.evaluate(&context).unwrap(); + assert_eq!(result, true); + } +} diff --git a/src/compilation/compiler.rs b/src/compilation/compiler.rs new file mode 100644 index 0000000..6fe349a --- /dev/null +++ b/src/compilation/compiler.rs @@ -0,0 +1,1272 @@ +use std::{collections::HashMap, num::NonZeroU64, sync::Arc}; + +use bigdecimal::{ + num_bigint::{BigInt, ToBigInt, TryFromBigIntError}, + BigDecimal, RoundingMode, +}; + +use crate::{ + bool_formula::Comparator, + generic::LocalDefinition, + types::{AnyType, BoolType, DateTimeType, DateType, DecimalType, IntType, StringType, Type}, + value::EnumValueFormula, + BoolFormulas, DecimalFormulas, Formula, IntFormulas, ObjectFormulas, StringFormulas, +}; + +use super::{ + parser::{parse, BinaryOp, ExpressionTree, ParsedExpression, UnaryOp}, + typed_formula::TypedFormula, + CompileError, CompileResult, FullType, MethodHeader, ObjectTypeDefinition, StaticMethod, +}; + +pub fn compile_formula( + formula: &str, + root: &dyn ObjectTypeDefinition, +) -> CompileResult { + let parsed = parse(formula)?; + let root = Root::new(root); + compile(parsed, &root, None) +} + +pub fn compile_formula_as( + formula: &str, + root: &dyn ObjectTypeDefinition, + type_: Type, +) -> CompileResult { + let parsed = parse(formula)?; + let root = Root::new(root); + compile(parsed, &root, Some(type_)) +} + +struct CompileContext<'a> { + root: &'a dyn ObjectTypeDefinition, + locals: HashMap, +} + +impl<'a> CompileContext<'a> { + fn new(root: &'a dyn ObjectTypeDefinition) -> Self { + Self { + root, + locals: HashMap::new(), + } + } + + fn get_local(&self, name: &str) -> Option<&Local> { + self.locals.get(name) + } + + fn add_local(&mut self, name: String, type_: FullType) { + let index = self.locals.len() as u32; + let local = Local { index, type_ }; + self.locals.insert(name, local); + } +} + +struct Local { + index: u32, + type_: FullType, +} + +struct Root<'a> { + specified_root: &'a dyn ObjectTypeDefinition, + builtin_methods: HashMap<&'static str, Vec>, + builtin_methods_by_id: HashMap, +} + +struct BuiltinMethod { + header: MethodHeader, + method: Arc) -> CompileResult>, +} + +impl BuiltinMethod { + fn new( + header: MethodHeader, + method: impl Fn(Vec) -> CompileResult + 'static, + ) -> Self { + Self { + header, + method: Arc::new(method), + } + } + + fn unary( + id: &'static str, + result: Type, + method: impl Fn(Box>) -> Box> + 'static, + ) -> Self { + Self::new( + MethodHeader::new(id.to_string(), result, vec![T::TYPE]), + move |args| { + let value = unary_args::(args)?; + TypedFormula::from::(method(value)) + }, + ) + } + + fn binary( + id: &'static str, + result: Type, + method: impl Fn(Box>, Box>) -> Box> + + 'static, + ) -> Self { + Self::new( + MethodHeader::new(id.to_string(), result, vec![A::TYPE, B::TYPE]), + move |args| { + let (a, b) = binary_args::(args)?; + TypedFormula::from::(method(a, b)) + }, + ) + } +} + +impl StaticMethod for BuiltinMethod { + fn get_header(&self) -> &MethodHeader { + &self.header + } + + fn call(&self, args: Vec) -> CompileResult { + (self.method)(args) + } +} + +fn unary_args(mut args: Vec) -> CompileResult>> { + if args.len() != 1 { + Err(CompileError::WrongNumberOfArguments { + expected: 1, + found: args.len(), + }) + } else { + let value = args.remove(0); + let type_ = value.type_.simplified(); + T::cast_from_formula(value.value).ok_or(CompileError::InvalidArgumentType { + expected: T::TYPE, + found: type_, + }) + } +} + +fn binary_args( + mut args: Vec, +) -> CompileResult<(Box>, Box>)> { + if args.len() != 2 { + Err(CompileError::WrongNumberOfArguments { + expected: 2, + found: args.len(), + }) + } else { + let a = args.remove(0); + let a_type = a.type_.simplified(); + let a = A::cast_from_formula(a.value).ok_or(CompileError::InvalidArgumentType { + expected: A::TYPE, + found: a_type, + })?; + let b = args.remove(0); + let b_type = b.type_.simplified(); + let b = B::cast_from_formula(b.value).ok_or(CompileError::InvalidArgumentType { + expected: B::TYPE, + found: b_type, + })?; + Ok((a, b)) + } +} + +impl<'a> Root<'a> { + fn new(specified_root: &'a dyn ObjectTypeDefinition) -> Self { + let mut builtin_methods = HashMap::new(); + builtin_methods.insert( + "abs".into(), + vec![ + BuiltinMethod::unary::("abs_int", Type::Int, |value| { + IntFormulas::abs(value) + }), + BuiltinMethod::unary::( + "abs_decimal", + Type::Decimal, + |value| DecimalFormulas::abs(value), + ), + ], + ); + builtin_methods.insert( + "min".into(), + vec![ + BuiltinMethod::binary::("min_int", Type::Int, |a, b| { + IntFormulas::min(a, b) + }), + BuiltinMethod::binary::( + "min_decimal", + Type::Decimal, + |a, b| DecimalFormulas::min(a, b), + ), + ], + ); + builtin_methods.insert( + "max".into(), + vec![ + BuiltinMethod::binary::("max_int", Type::Int, |a, b| { + IntFormulas::max(a, b) + }), + BuiltinMethod::binary::( + "max_decimal", + Type::Decimal, + |a, b| DecimalFormulas::max(a, b), + ), + ], + ); + builtin_methods.insert( + "sqrt".into(), + vec![ + BuiltinMethod::unary::( + "sqrt_decimal", + Type::Decimal, + |value| { + DecimalFormulas::sqrt( + value, + NonZeroU64::new(10).unwrap(), + RoundingMode::HalfEven, + ) + }, + ), + BuiltinMethod::new( + MethodHeader::new( + "sqrt_int".to_string(), + Type::Decimal, + vec![Type::Decimal, Type::Int], + ), + |mut args| { + let a = args.remove(0); + let b = args.remove(1); + let a = a.value.to_decimal().expect("decimal formula expected"); + let precision = b.value.to_int_constant().expect("int constant expected"); + TypedFormula::from::(DecimalFormulas::sqrt( + a, + NonZeroU64::new(precision as u64).unwrap(), + RoundingMode::HalfEven, + )) + }, + ), + ], + ); + builtin_methods.insert( + "floor".into(), + vec![BuiltinMethod::unary::( + "floor", + Type::Int, + |value| IntFormulas::floor(value), + )], + ); + builtin_methods.insert( + "ceil".into(), + vec![BuiltinMethod::unary::( + "ceil", + Type::Int, + |value| IntFormulas::ceil(value), + )], + ); + + builtin_methods.insert( + "days_between", + vec![ + BuiltinMethod::binary::( + "days_between_date", + Type::Int, + |a, b| IntFormulas::days_between(a, b), + ), + BuiltinMethod::binary::( + "days_between_datetime", + Type::Int, + |a, b| IntFormulas::days_between_dt(a, b), + ), + ], + ); + builtin_methods.insert( + "months_between", + vec![ + BuiltinMethod::binary::( + "months_between_date", + Type::Int, + |a, b| IntFormulas::months_between(a, b), + ), + BuiltinMethod::binary::( + "months_between_datetime", + Type::Int, + |a, b| IntFormulas::months_between_dt(a, b), + ), + ], + ); + builtin_methods.insert( + "years_between", + vec![ + BuiltinMethod::binary::( + "years_between_date", + Type::Int, + |a, b| IntFormulas::years_between(a, b), + ), + BuiltinMethod::binary::( + "years_between_datetime", + Type::Int, + |a, b| IntFormulas::years_between_dt(a, b), + ), + ], + ); + builtin_methods.insert( + "hours_between", + vec![ + BuiltinMethod::binary::( + "hours_between", + Type::Int, + |a, b| IntFormulas::hours_between(a, b), + ), + ], + ); + builtin_methods.insert( + "minutes_between", + vec![ + BuiltinMethod::binary::( + "minutes_between", + Type::Int, + |a, b| IntFormulas::minutes_between(a, b), + ), + ], + ); + builtin_methods.insert( + "seconds_between", + vec![ + BuiltinMethod::binary::( + "seconds_between", + Type::Int, + |a, b| IntFormulas::seconds_between(a, b), + ), + ], + ); + builtin_methods.insert( + "millis_between", + vec![ + BuiltinMethod::binary::( + "millis_between", + Type::Int, + |a, b| IntFormulas::milliseconds_between(a, b), + ), + ], + ); + + builtin_methods.insert( + "int", + vec![ + BuiltinMethod::unary::( + "int_from_decimal", + Type::Int, + |value| IntFormulas::floor(value), + ), + BuiltinMethod::unary::( + "int_from_string", + Type::Int, + |value| IntFormulas::from_string(value), + ), + ], + ); + builtin_methods.insert( + "decimal", + vec![ + BuiltinMethod::unary::( + "decimal_from_int", + Type::Decimal, + |value| DecimalFormulas::from_int(value), + ), + BuiltinMethod::unary::( + "decimal_from_string", + Type::Decimal, + |value| DecimalFormulas::from_string(value), + ), + ], + ); + builtin_methods.insert( + "string", + vec![ + BuiltinMethod::unary::( + "string_from_bool", + Type::String, + |value| StringFormulas::from_bool(value), + ), + BuiltinMethod::unary::( + "string_from_int", + Type::String, + |value| StringFormulas::from_int(value), + ), + BuiltinMethod::unary::( + "string_from_decimal", + Type::String, + |value| StringFormulas::from_decimal(value), + ), + ], + ); + + let mut builtin_methods_by_id = HashMap::new(); + for (name, methods) in &builtin_methods { + for method in methods { + builtin_methods_by_id.insert(method.header.method_id.clone(), *name); + } + } + + Self { + specified_root, + builtin_methods, + builtin_methods_by_id, + } + } +} + +impl<'a> ObjectTypeDefinition for Root<'a> { + fn get_field_type(&self, name: &str) -> Option { + self.specified_root.get_field_type(name) + } + + fn get_method_headers(&self, name: &str) -> Option> { + self.builtin_methods + .get(name) + .map(|x| x.iter().map(|x| &x.header).collect()) + } + + fn get_static_method(&self, id: &str) -> Option<&dyn super::StaticMethod> { + let method_name = self.builtin_methods_by_id.get(id)?; + self.builtin_methods + .get(method_name) + .and_then(|x| x.iter().find(|x| x.header.method_id == id)) + .map(|x| x as &dyn StaticMethod) + } + + fn get_instance_method(&self, _id: &str) -> Option<&dyn super::InstanceMethod> { + None + } +} + +fn compile( + parsed: ParsedExpression, + root: &Root, + as_type: Option, +) -> CompileResult { + let mut context = CompileContext::new(root); + let mut locals = Vec::new(); + for local in parsed.locals { + let local_value = compile_expression(local.value, &context, None)?; + context.add_local(local.name.clone(), local_value.type_); + locals.push(LocalDefinition::new(local.name, local_value.value)); + } + let result = compile_expression(parsed.formula, &context, as_type)?; + if locals.is_empty() { + Ok(result.value) + } else { + Ok(result.value.with_locals(locals)) + } +} + +fn compile_expression( + tree: ExpressionTree, + context: &CompileContext, + as_type: Option, +) -> CompileResult { + match tree { + ExpressionTree::Number(value) => compile_number(value, as_type), + ExpressionTree::Identifier(name) => compile_identifier(name, context, as_type), + ExpressionTree::String(value) => compile_string(value, as_type), + ExpressionTree::Conditional { + condition, + then, + else_, + } => compile_conditional(*condition, *then, *else_, context, as_type), + ExpressionTree::UnaryOp { op, value } => compile_unary_op(op, *value, context, as_type), + ExpressionTree::BinaryOp { op, left, right } => { + compile_binary_op(op, *left, *right, context, as_type) + } + ExpressionTree::GetField { object, field } => compile_get_field(*object, field, context), + ExpressionTree::Call { target, args } => compile_call(*target, args, context), + } +} + +fn compile_expression_type( + tree: &ExpressionTree, + context: &CompileContext, + as_type: Option, +) -> CompileResult { + match tree { + ExpressionTree::Number(value) => compile_number_type(value, as_type), + ExpressionTree::Identifier(name) => compile_identifier_type(name, context, as_type), + ExpressionTree::String(value) => compile_string_type(value, as_type), + ExpressionTree::Conditional { then, else_, .. } => { + compile_conditional_type(then, else_, context, as_type) + } + ExpressionTree::UnaryOp { op, value } => { + compile_unary_op_type(*op, value, context, as_type) + } + ExpressionTree::BinaryOp { op, left, right } => { + compile_binary_op_type(*op, left, right, context, as_type) + } + ExpressionTree::GetField { object, field } => { + compile_get_field_type(object, field.clone(), context) + } + ExpressionTree::Call { target, args } => compile_call_type(target, args, context), + } +} + +fn compile_number(value: BigDecimal, as_type: Option) -> CompileResult { + if let Some(as_type) = as_type { + match as_type { + Type::Int => { + let value = value + .to_bigint() + .ok_or(CompileError::IntegerMustBeIntegral(value))?; + Ok(TypedFormula { + type_: FullType::Basic(Type::Int), + value: EnumValueFormula::IntConstant(value.try_into().map_err( + |e: TryFromBigIntError| { + CompileError::IntegerTooLarge(e.into_original()) + }, + )?), + }) + } + Type::Decimal => Ok(TypedFormula { + type_: FullType::Basic(Type::Decimal), + value: EnumValueFormula::Decimal(DecimalFormulas::value(value)), + }), + type_ => Err(CompileError::CannotCompileNumberAsType(value, type_)), + } + } else if value.is_integer() { + let value = value + .to_bigint() + .ok_or(CompileError::IntegerMustBeIntegral(value))?; + Ok(TypedFormula { + type_: FullType::Basic(Type::Int), + value: EnumValueFormula::IntConstant(value.try_into().map_err( + |e: TryFromBigIntError| CompileError::IntegerTooLarge(e.into_original()), + )?), + }) + } else { + Ok(TypedFormula { + type_: FullType::Basic(Type::Decimal), + value: EnumValueFormula::Decimal(DecimalFormulas::value(value)), + }) + } +} + +fn compile_number_type(value: &BigDecimal, as_type: Option) -> CompileResult { + if let Some(as_type) = as_type { + match as_type { + Type::Int => Ok(FullType::Basic(Type::Int)), + Type::Decimal => Ok(FullType::Basic(Type::Decimal)), + type_ => Err(CompileError::CannotCompileNumberAsType( + value.clone(), + type_, + )), + } + } else if value.is_integer() { + Ok(FullType::Basic(Type::Int)) + } else { + Ok(FullType::Basic(Type::Decimal)) + } +} + +fn compile_identifier( + name: String, + context: &CompileContext, + as_type: Option, +) -> CompileResult { + if let Some(local) = context.get_local(&name) { + return if let Some(as_type) = as_type { + if local.type_.matches(&as_type) { + Ok(TypedFormula { + type_: local.type_.clone(), + value: EnumValueFormula::local(local.type_.simplified(), local.index), + }) + } else { + Err(CompileError::InvalidLocalType { + name, + expected: as_type, + found: local.type_.simplified(), + }) + } + } else { + Ok(TypedFormula { + type_: local.type_.clone(), + value: EnumValueFormula::local(local.type_.simplified(), local.index), + }) + }; + } + + let field_type = context.root.get_field_type(&name); + if let Some(field_type) = field_type { + return Ok(TypedFormula { + type_: field_type.clone(), + value: EnumValueFormula::field(field_type.simplified(), ObjectFormulas::root(), name), + }); + } + + Err(CompileError::VariableNotFound(name)) +} + +fn compile_identifier_type( + name: &str, + context: &CompileContext, + as_type: Option, +) -> CompileResult { + if let Some(local) = context.get_local(&name) { + return if let Some(as_type) = as_type { + if local.type_.matches(&as_type) { + Ok(local.type_.clone()) + } else { + Err(CompileError::InvalidLocalType { + name: name.to_string(), + expected: as_type, + found: local.type_.simplified(), + }) + } + } else { + Ok(local.type_.clone()) + }; + } + + let field_type = context.root.get_field_type(&name); + if let Some(field_type) = field_type { + return Ok(field_type.clone()); + } + + Err(CompileError::VariableNotFound(name.to_string())) +} + +fn compile_string(value: String, as_type: Option) -> CompileResult { + if let Some(as_type) = as_type { + match as_type { + Type::String => Ok(TypedFormula { + type_: FullType::Basic(Type::String), + value: EnumValueFormula::String(StringFormulas::value(value)), + }), + type_ => Err(CompileError::CannotUseStringAsType(value, type_)), + } + } else { + Ok(TypedFormula { + type_: FullType::Basic(Type::String), + value: EnumValueFormula::String(StringFormulas::value(value)), + }) + } +} + +fn compile_string_type(value: &str, as_type: Option) -> CompileResult { + if let Some(as_type) = as_type { + match as_type { + Type::String => Ok(FullType::Basic(Type::String)), + type_ => Err(CompileError::CannotUseStringAsType( + value.to_string(), + type_, + )), + } + } else { + Ok(FullType::Basic(Type::String)) + } +} + +fn compile_conditional( + condition: ExpressionTree, + then: ExpressionTree, + else_: ExpressionTree, + context: &CompileContext, + as_type: Option, +) -> CompileResult { + let condition = compile_expression(condition, context, Some(Type::Bool))?; + let then = compile_expression(then, context, as_type.clone())?; + let else_ = compile_expression(else_, context, as_type)?; + if then.type_.matches(&else_.type_.simplified()) { + Ok(TypedFormula { + type_: then.type_.clone(), + value: EnumValueFormula::conditional( + then.type_.simplified(), + condition + .value + .to_bool() + .ok_or(CompileError::ConditionMustBeBool)?, + then.value, + else_.value, + ), + }) + } else { + Err(CompileError::ConditionalBranchesMustHaveSameType( + then.type_.simplified(), + else_.type_.simplified(), + )) + } +} + +fn compile_conditional_type( + then: &ExpressionTree, + else_: &ExpressionTree, + context: &CompileContext, + as_type: Option, +) -> CompileResult { + let then = compile_expression_type(then, context, as_type.clone())?; + let else_ = compile_expression_type(else_, context, as_type)?; + if then.matches(&else_.simplified()) { + Ok(then) + } else { + Err(CompileError::ConditionalBranchesMustHaveSameType( + then.simplified(), + else_.simplified(), + )) + } +} + +fn compile_unary_op( + op: UnaryOp, + value: ExpressionTree, + context: &CompileContext, + as_type: Option, +) -> CompileResult { + let value = compile_expression(value, context, as_type)?; + match op { + UnaryOp::Not => Ok(TypedFormula { + type_: FullType::Basic(Type::Bool), + value: EnumValueFormula::Bool(BoolFormulas::not(value.value.to_bool().ok_or( + CompileError::InvalidUnaryOperand(op, value.type_.simplified()), + )?)), + }), + UnaryOp::Minus => match value.type_.simplified() { + Type::Int => Ok(TypedFormula { + type_: FullType::Basic(Type::Int), + value: EnumValueFormula::Int(IntFormulas::negate( + value.value.to_int().expect("int formula expected"), + )), + }), + Type::Decimal => Ok(TypedFormula { + type_: FullType::Basic(Type::Decimal), + value: EnumValueFormula::Decimal(DecimalFormulas::negate( + value.value.to_decimal().expect("decimal formula expected"), + )), + }), + type_ => Err(CompileError::InvalidUnaryOperand(op, type_)), + }, + } +} + +fn compile_unary_op_type( + op: UnaryOp, + value: &ExpressionTree, + context: &CompileContext, + as_type: Option, +) -> CompileResult { + let value = compile_expression_type(value, context, as_type)?; + match op { + UnaryOp::Not => Ok(FullType::Basic(Type::Bool)), + UnaryOp::Minus => match value.simplified() { + Type::Int => Ok(FullType::Basic(Type::Int)), + Type::Decimal => Ok(FullType::Basic(Type::Decimal)), + type_ => Err(CompileError::InvalidUnaryOperand(op, type_)), + }, + } +} + +fn compile_binary_op( + op: BinaryOp, + left: ExpressionTree, + right: ExpressionTree, + context: &CompileContext, + as_type: Option, +) -> CompileResult { + let left_type = match op { + BinaryOp::Plus + | BinaryOp::Minus + | BinaryOp::Times + | BinaryOp::Divide + | BinaryOp::Modulo => as_type, + BinaryOp::Equals + | BinaryOp::NotEquals + | BinaryOp::LessThan + | BinaryOp::LessThanOrEqual + | BinaryOp::GreaterThan + | BinaryOp::GreaterThanOrEqual => None, + BinaryOp::Or | BinaryOp::And => Some(Type::Bool), + BinaryOp::Index => as_type.map(|t| t.array()).flatten(), + }; + let left = compile_expression(left, context, left_type)?; + + let right_type = Some(if op == BinaryOp::Index { + left.type_.simplified() + } else { + Type::Int + }); + let right = compile_expression(right, context, right_type)?; + match op { + BinaryOp::Plus => match (left.type_.simplified(), right.type_.simplified()) { + (Type::Int, Type::Int) => Ok(TypedFormula { + type_: FullType::Basic(Type::Int), + value: EnumValueFormula::Int(IntFormulas::add( + left.value.to_int().expect("int formula expected"), + right.value.to_int().expect("int formula expected"), + )), + }), + (Type::Decimal, Type::Decimal) + | (Type::Int, Type::Decimal) + | (Type::Decimal, Type::Int) => Ok(TypedFormula { + type_: FullType::Basic(Type::Decimal), + value: EnumValueFormula::Decimal(DecimalFormulas::add( + left.value.to_decimal().expect("decimal formula expected"), + right.value.to_decimal().expect("decimal formula expected"), + )), + }), + (Type::String, Type::String) + | (Type::Int, Type::String) + | (Type::Decimal, Type::String) + | (Type::String, Type::Int) + | (Type::String, Type::Decimal) => Ok(TypedFormula { + type_: FullType::Basic(Type::String), + value: EnumValueFormula::String(StringFormulas::concat( + left.value.to_string().expect("string formula expected"), + right.value.to_string().expect("string formula expected"), + )), + }), + (type1, type2) => Err(CompileError::InvalidBinaryOperand(op, type1, type2)), + }, + BinaryOp::Minus => match (left.type_.simplified(), right.type_.simplified()) { + (Type::Int, Type::Int) => Ok(TypedFormula { + type_: FullType::Basic(Type::Int), + value: EnumValueFormula::Int(IntFormulas::subtract( + left.value.to_int().expect("int formula expected"), + right.value.to_int().expect("int formula expected"), + )), + }), + (Type::Decimal, Type::Decimal) + | (Type::Decimal, Type::Int) + | (Type::Int, Type::Decimal) => Ok(TypedFormula { + type_: FullType::Basic(Type::Decimal), + value: EnumValueFormula::Decimal(DecimalFormulas::subtract( + left.value.to_decimal().expect("decimal formula expected"), + right.value.to_decimal().expect("decimal formula expected"), + )), + }), + (type1, type2) => Err(CompileError::InvalidBinaryOperand(op, type1, type2)), + }, + BinaryOp::Times => match (left.type_.simplified(), right.type_.simplified()) { + (Type::Int, Type::Int) => Ok(TypedFormula { + type_: FullType::Basic(Type::Int), + value: EnumValueFormula::Int(IntFormulas::multiply( + left.value.to_int().expect("int formula expected"), + right.value.to_int().expect("int formula expected"), + )), + }), + (Type::Decimal, Type::Decimal) + | (Type::Decimal, Type::Int) + | (Type::Int, Type::Decimal) => Ok(TypedFormula { + type_: FullType::Basic(Type::Decimal), + value: EnumValueFormula::Decimal(DecimalFormulas::multiply( + left.value.to_decimal().expect("decimal formula expected"), + right.value.to_decimal().expect("decimal formula expected"), + )), + }), + (type1, type2) => Err(CompileError::InvalidBinaryOperand(op, type1, type2)), + }, + BinaryOp::Divide => match (left.type_.simplified(), right.type_.simplified()) { + (Type::Int, Type::Int) => Ok(TypedFormula { + type_: FullType::Basic(Type::Int), + value: EnumValueFormula::Int(IntFormulas::divide( + left.value.to_int().expect("int formula expected"), + right.value.to_int().expect("int formula expected"), + )), + }), + (Type::Decimal, Type::Decimal) + | (Type::Decimal, Type::Int) + | (Type::Int, Type::Decimal) => Ok(TypedFormula { + type_: FullType::Basic(Type::Decimal), + value: EnumValueFormula::Decimal(DecimalFormulas::divide( + left.value.to_decimal().expect("decimal formula expected"), + right.value.to_decimal().expect("decimal formula expected"), + NonZeroU64::new(10).unwrap(), + RoundingMode::HalfEven, + )), + }), + (type1, type2) => Err(CompileError::InvalidBinaryOperand(op, type1, type2)), + }, + BinaryOp::Modulo => match (left.type_.simplified(), right.type_.simplified()) { + (Type::Int, Type::Int) => Ok(TypedFormula { + type_: FullType::Basic(Type::Int), + value: EnumValueFormula::Int(IntFormulas::modulo( + left.value.to_int().expect("int formula expected"), + right.value.to_int().expect("int formula expected"), + )), + }), + (type1, type2) => Err(CompileError::InvalidBinaryOperand(op, type1, type2)), + }, + BinaryOp::Equals => compare(Comparator::Equal, left, right), + BinaryOp::NotEquals => compare(Comparator::NotEqual, left, right), + BinaryOp::LessThan => compare(Comparator::LessThan, left, right), + BinaryOp::LessThanOrEqual => compare(Comparator::LessThanOrEqual, left, right), + BinaryOp::GreaterThan => compare(Comparator::GreaterThan, left, right), + BinaryOp::GreaterThanOrEqual => compare(Comparator::GreaterThanOrEqual, left, right), + BinaryOp::Or => { + let left = left.require_bool()?; + let right = right.require_bool()?; + Ok(TypedFormula { + type_: FullType::Basic(Type::Bool), + value: EnumValueFormula::Bool(BoolFormulas::or(left, right)), + }) + } + BinaryOp::And => { + let left = left.require_bool()?; + let right = right.require_bool()?; + Ok(TypedFormula { + type_: FullType::Basic(Type::Bool), + value: EnumValueFormula::Bool(BoolFormulas::and(left, right)), + }) + } + BinaryOp::Index => { + let index = right + .value + .to_int() + .ok_or(CompileError::IndexMustBeInteger)?; + Ok(TypedFormula { + type_: left + .type_ + .array_element_type() + .ok_or(CompileError::ValueNotAnArray)?, + value: EnumValueFormula::array_element(left.value, index) + .ok_or(CompileError::ValueNotAnArray)?, + }) + } + } +} + +fn compile_binary_op_type( + op: BinaryOp, + left: &ExpressionTree, + right: &ExpressionTree, + context: &CompileContext, + as_type: Option, +) -> CompileResult { + let left_type = match op { + BinaryOp::Plus + | BinaryOp::Minus + | BinaryOp::Times + | BinaryOp::Divide + | BinaryOp::Modulo => as_type, + BinaryOp::Equals + | BinaryOp::NotEquals + | BinaryOp::LessThan + | BinaryOp::LessThanOrEqual + | BinaryOp::GreaterThan + | BinaryOp::GreaterThanOrEqual => None, + BinaryOp::Or | BinaryOp::And => Some(Type::Bool), + BinaryOp::Index => as_type.map(|t| t.array()).flatten(), + }; + let left = compile_expression_type(left, context, left_type)?; + + let right_type = Some(if op == BinaryOp::Index { + left.simplified() + } else { + Type::Int + }); + let right = compile_expression_type(right, context, right_type)?; + match op { + BinaryOp::Plus => match (left.simplified(), right.simplified()) { + (Type::Int, Type::Int) => Ok(FullType::Basic(Type::Int)), + (Type::Decimal, Type::Decimal) + | (Type::Int, Type::Decimal) + | (Type::Decimal, Type::Int) => Ok(FullType::Basic(Type::Decimal)), + (Type::String, Type::String) + | (Type::Int, Type::String) + | (Type::Decimal, Type::String) + | (Type::String, Type::Int) + | (Type::String, Type::Decimal) => Ok(FullType::Basic(Type::String)), + (type1, type2) => Err(CompileError::InvalidBinaryOperand(op, type1, type2)), + }, + BinaryOp::Minus | BinaryOp::Times | BinaryOp::Divide => { + match (left.simplified(), right.simplified()) { + (Type::Int, Type::Int) => Ok(FullType::Basic(Type::Int)), + (Type::Decimal, Type::Decimal) + | (Type::Decimal, Type::Int) + | (Type::Int, Type::Decimal) => Ok(FullType::Basic(Type::Decimal)), + (type1, type2) => Err(CompileError::InvalidBinaryOperand(op, type1, type2)), + } + } + BinaryOp::Modulo => match (left.simplified(), right.simplified()) { + (Type::Int, Type::Int) => Ok(FullType::Basic(Type::Int)), + (type1, type2) => Err(CompileError::InvalidBinaryOperand(op, type1, type2)), + }, + BinaryOp::Equals + | BinaryOp::NotEquals + | BinaryOp::LessThan + | BinaryOp::LessThanOrEqual + | BinaryOp::GreaterThan + | BinaryOp::GreaterThanOrEqual + | BinaryOp::Or + | BinaryOp::And => Ok(FullType::Basic(Type::Bool)), + BinaryOp::Index => Ok(left + .array_element_type() + .ok_or(CompileError::ValueNotAnArray)?), + } +} + +fn compare( + comparison: Comparator, + left: TypedFormula, + right: TypedFormula, +) -> CompileResult { + match (left.type_.simplified(), right.type_.simplified()) { + (Type::Bool, Type::Bool) => match comparison { + Comparator::Equal => Ok(TypedFormula { + type_: FullType::Basic(Type::Bool), + value: EnumValueFormula::Bool(BoolFormulas::not(BoolFormulas::xor( + left.value.to_bool().expect("bool formula expected"), + right.value.to_bool().expect("bool formula expected"), + ))), + }), + Comparator::NotEqual => Ok(TypedFormula { + type_: FullType::Basic(Type::Bool), + value: EnumValueFormula::Bool(BoolFormulas::xor( + left.value.to_bool().expect("bool formula expected"), + right.value.to_bool().expect("bool formula expected"), + )), + }), + _ => Err(CompileError::InvalidComparator( + comparison, + left.type_.simplified(), + right.type_.simplified(), + )), + }, + (Type::Int, Type::Int) => Ok(TypedFormula { + type_: FullType::Basic(Type::Bool), + value: EnumValueFormula::Bool(BoolFormulas::compare_int( + left.value.to_int().expect("int formula expected"), + right.value.to_int().expect("int formula expected"), + comparison, + )), + }), + (Type::Decimal, Type::Decimal) => Ok(TypedFormula { + type_: FullType::Basic(Type::Bool), + value: EnumValueFormula::Bool(BoolFormulas::compare_decimal( + left.value.to_decimal().expect("decimal formula expected"), + right.value.to_decimal().expect("decimal formula expected"), + comparison, + )), + }), + (Type::String, Type::String) => Ok(TypedFormula { + type_: FullType::Basic(Type::Bool), + value: EnumValueFormula::Bool(BoolFormulas::compare_string( + left.value.to_string().expect("string formula expected"), + right.value.to_string().expect("string formula expected"), + comparison, + )), + }), + (Type::Date, Type::Date) => Ok(TypedFormula { + type_: FullType::Basic(Type::Bool), + value: EnumValueFormula::Bool(BoolFormulas::compare_date( + left.value.to_date().expect("date formula expected"), + right.value.to_date().expect("date formula expected"), + comparison, + )), + }), + (Type::DateTime, Type::DateTime) => Ok(TypedFormula { + type_: FullType::Basic(Type::Bool), + value: EnumValueFormula::Bool(BoolFormulas::compare_datetime( + left.value.to_datetime().expect("datetime formula expected"), + right + .value + .to_datetime() + .expect("datetime formula expected"), + comparison, + )), + }), + (type_1, type_2) => Err(CompileError::InvalidComparator(comparison, type_1, type_2)), + } +} + +fn compile_get_field( + object: ExpressionTree, + field: String, + context: &CompileContext, +) -> CompileResult { + let object = compile_expression(object, context, None)?; + //let object_definition: Box = object.type_.get_definition(); + unimplemented!() +} + +fn compile_get_field_type( + object: &ExpressionTree, + field: String, + context: &CompileContext, +) -> CompileResult { + let object = compile_expression_type(object, context, None)?; + //let object_definition: Box = object.type_.get_definition(); + unimplemented!() +} + +fn compile_call( + target: ExpressionTree, + args: Vec>, + context: &CompileContext, +) -> CompileResult { + match target { + ExpressionTree::Identifier(name) => compile_function_call(name, args, context), + ExpressionTree::GetField { object, field } => { + let object = compile_expression(*object, context, None)?; + compile_method_call(object, field, args, context) + } + _other => Err(CompileError::InvalidMethodTarget), + } +} + +fn compile_call_type( + target: &ExpressionTree, + args: &Vec>, + context: &CompileContext, +) -> CompileResult { + match target { + ExpressionTree::Identifier(name) => compile_function_call_type(name, args, context), + ExpressionTree::GetField { object, field } => { + let object = compile_expression_type(object, context, None)?; + compile_method_call_type(object, field, args, context) + } + _other => Err(CompileError::InvalidMethodTarget), + } +} + +fn compile_function_call( + name: String, + args: Vec>, + context: &CompileContext, +) -> CompileResult { + let method_headers = context.root.get_method_headers(&name); + if let Some(method_headers) = method_headers { + let method_header = pick_method_header(context, &name, method_headers, &args)?; + let mut method_args = Vec::new(); + for (i, arg) in args.into_iter().enumerate() { + method_args.push(compile_expression( + *arg, + context, + Some(method_header.argument_types[i].simplified()), + )?); + } + + let method = context.root.get_static_method(&method_header.method_id); + if let Some(method) = method { + method.call(method_args) + } else { + Err(CompileError::MethodNotFound { name }) + } + } else { + Err(CompileError::MethodNotFound { name }) + } +} + +fn compile_function_call_type( + name: &str, + args: &Vec>, + context: &CompileContext, +) -> CompileResult { + let method_headers = context.root.get_method_headers(&name); + if let Some(method_headers) = method_headers { + let method_header = pick_method_header(context, &name, method_headers, &args)?; + let mut method_args = Vec::new(); + for (i, arg) in args.into_iter().enumerate() { + method_args.push(compile_expression_type( + arg, + context, + Some(method_header.argument_types[i].simplified()), + )?); + } + + Ok(method_header.return_type.clone()) + } else { + Err(CompileError::MethodNotFound { + name: name.to_string(), + }) + } +} + +fn compile_method_call( + object: TypedFormula, + name: String, + args: Vec>, + context: &CompileContext, +) -> CompileResult { + let object_type = object.type_.object().ok_or(CompileError::NotAnObject)?; + let method_headers = object_type.get_method_headers(&name); + if let Some(method_headers) = method_headers { + let selected = pick_method_header(context, &name, method_headers, &args)?; + let mut method_args = Vec::new(); + for (i, arg) in args.into_iter().enumerate() { + let arg_type = + compile_expression(*arg, context, Some(selected.argument_types[i].simplified()))?; + method_args.push(arg_type); + } + let method = context.root.get_instance_method(&selected.method_id); + if let Some(method) = method { + method.call(object, method_args) + } else { + Err(CompileError::MethodNotFound { name }) + } + } else { + Err(CompileError::MethodNotFound { name }) + } +} + +fn compile_method_call_type( + object: FullType, + name: &str, + args: &Vec>, + context: &CompileContext, +) -> CompileResult { + let object_type = object.object().ok_or(CompileError::NotAnObject)?; + let method_headers = object_type.get_method_headers(name); + if let Some(method_headers) = method_headers { + let selected = pick_method_header(context, name, method_headers, &args)?; + let mut method_args = Vec::new(); + for (i, arg) in args.into_iter().enumerate() { + let arg_type = compile_expression_type( + arg, + context, + Some(selected.argument_types[i].simplified()), + )?; + method_args.push(arg_type); + } + Ok(selected.return_type.clone()) + } else { + Err(CompileError::MethodNotFound { + name: name.to_string(), + }) + } +} + +fn pick_method_header<'a>( + context: &CompileContext, + name: &str, + method_headers: Vec<&'a MethodHeader>, + args: &Vec>, +) -> CompileResult<&'a MethodHeader> { + let mut candidates = Vec::new(); + for method_header in method_headers { + if method_header.argument_types.len() == args.len() { + let mut candidate = true; + for (i, arg) in args.into_iter().enumerate() { + let arg_type = compile_expression_type( + &arg, + context, + Some(method_header.argument_types[i].simplified()), + )?; + if !method_header.argument_types[i].matches(&arg_type.simplified()) { + candidate = false; + break; + } + } + if candidate { + candidates.push(method_header); + } + } + } + if candidates.is_empty() { + Err(CompileError::NoMatchingMethod { + name: name.to_string(), + args: args.len(), + }) + } else if candidates.len() > 1 { + Err(CompileError::AmbiguousMethodCall { + name: name.to_string(), + args: args.len(), + }) + } else { + Ok(candidates.remove(0)) + } +} diff --git a/src/compilation/lexer.rs b/src/compilation/lexer.rs new file mode 100644 index 0000000..302761e --- /dev/null +++ b/src/compilation/lexer.rs @@ -0,0 +1,172 @@ +use std::iter::Peekable; + +use logos::{Lexer, Logos}; + +use super::{CompileError, CompileResult}; + +#[derive(Logos, Debug, PartialEq)] +#[logos(skip r"[ \t\r\n]+")] +pub enum Token<'a> { + #[regex(r"[a-zA-Z_][a-zA-Z0-9_]*", |lex| lex.slice())] + Identifier(&'a str), + + #[regex(r"[0-9]+(\.[0-9]+)?", |lex| lex.slice())] + Number(&'a str), + + #[regex(r#""([^"\\]|\\.)*""#, |lex| lex.slice())] + String(&'a str), + + #[token("(")] + OpenParen, + + #[token(")")] + CloseParen, + + #[token("[")] + OpenBracket, + + #[token("]")] + CloseBracket, + + #[token("+")] + Plus, + + #[token("-")] + Minus, + + #[token("*")] + Times, + + #[token("/")] + Divide, + + #[token("%")] + Modulo, + + #[token(".")] + Dot, + + #[token(":")] + Colon, + + #[token(",")] + Comma, + + #[token("=")] + Equals, + + #[token("==")] + DoubleEquals, + + #[token("!=")] + NotEquals, + + #[token("<>")] + NotEqualsAlt, + + #[token("<")] + LessThan, + + #[token("<=")] + LessThanOrEqual, + + #[token(">")] + GreaterThan, + + #[token(">=")] + GreaterThanOrEqual, +} + +impl<'a> Token<'a> { + pub fn to_string(&self) -> String { + match self { + Token::Identifier(value) => value.to_string(), + Token::Number(value) => value.to_string(), + Token::String(value) => value.to_string(), + Token::OpenParen => "(".into(), + Token::CloseParen => ")".into(), + Token::OpenBracket => "[".into(), + Token::CloseBracket => "]".into(), + Token::Plus => "+".into(), + Token::Minus => "-".into(), + Token::Times => "*".into(), + Token::Divide => "/".into(), + Token::Modulo => "%".into(), + Token::Dot => ".".into(), + Token::Colon => ":".into(), + Token::Comma => ",".into(), + Token::Equals => "=".into(), + Token::DoubleEquals => "==".into(), + Token::NotEquals => "!=".into(), + Token::NotEqualsAlt => "<>".into(), + Token::LessThan => "<".into(), + Token::LessThanOrEqual => "<=".into(), + Token::GreaterThan => ">".into(), + Token::GreaterThanOrEqual => ">=".into(), + } + } +} + +impl<'a> Token<'a> { + pub fn require_identifier(self) -> CompileResult { + match self { + Token::Identifier(value) => Ok(value.to_string()), + _ => Err(CompileError::UnexpectedToken { + expected: "identifier".into(), + found: self.to_string(), + }), + } + } +} + +pub struct TokenStream<'a> { + lexer: Peekable>>, +} + +impl<'a> TokenStream<'a> { + pub fn new(source: &'a str) -> Self { + Self { + lexer: Token::lexer(source).peekable(), + } + } + + pub fn peek(&mut self) -> Option<&Token> { + let token = self.lexer.peek(); + if let Some(token) = token { + match token { + Ok(token) => Some(token), + Err(_) => None, + } + } else { + None + } + } + + pub fn next(&mut self) -> Option { + self.lexer.next().map(|x| x.unwrap()) + } + + pub fn expect(&mut self, token: Token) -> CompileResult<()> { + match self.next() { + Some(t) if t == token => Ok(()), + Some(t) => Err(CompileError::UnexpectedToken { + expected: token.to_string(), + found: t.to_string(), + }), + None => Err(CompileError::UnexpectedEndOfInput), + } + } + + pub fn maybe(&mut self, token: Token) -> bool { + if let Some(t) = self.peek() { + if *t == token { + self.next(); + true + } else { + false + } + } else { + false + } + } +} diff --git a/src/compilation/mod.rs b/src/compilation/mod.rs new file mode 100644 index 0000000..5995aac --- /dev/null +++ b/src/compilation/mod.rs @@ -0,0 +1,201 @@ +use std::sync::Arc; + +use bigdecimal::{num_bigint::BigInt, BigDecimal, ParseBigDecimalError}; + +use crate::{bool_formula::Comparator, types::Type}; + +use self::{ + parser::{BinaryOp, UnaryOp}, + typed_formula::TypedFormula, +}; + +mod compiler; +mod lexer; +mod parser; +mod typed_formula; + +#[derive(Debug)] +pub enum CompileError { + UnexpectedToken { + expected: String, + found: String, + }, + UnexpectedUnparsedToken { + found: String, + }, + UnexpectedEndOfInput, + CannotParseDecimal(ParseBigDecimalError), + IntegerMustBeIntegral(BigDecimal), + IntegerTooLarge(BigInt), + CannotCompileNumberAsType(BigDecimal, Type), + CannotUseStringAsType(String, Type), + WrongNumberOfArguments { + expected: usize, + found: usize, + }, + InvalidArgumentType { + expected: Type, + found: Type, + }, + InvalidLocalType { + name: String, + expected: Type, + found: Type, + }, + VariableNotFound(String), + ConditionMustBeBool, + ConditionalBranchesMustHaveSameType(Type, Type), + InvalidUnaryOperand(UnaryOp, Type), + InvalidBinaryOperand(BinaryOp, Type, Type), + InvalidComparator(Comparator, Type, Type), + IndexMustBeInteger, + ValueNotAnArray, + InvalidMethodTarget, + NotAnObject, + MethodNotFound { + name: String, + }, + NoMatchingMethod { + name: String, + args: usize, + }, + AmbiguousMethodCall { + name: String, + args: usize, + }, + NotABasicType { + found: Type, + }, +} + +pub type CompileResult = Result; + +#[derive(Clone)] +pub enum FullType { + Basic(Type), + Array(Box), + Object(Arc), + EnumConstant(Vec), +} + +impl FullType { + pub fn from_simple(t: Type) -> CompileResult { + match t { + Type::Bool | Type::Int | Type::Decimal | Type::String | Type::Date | Type::DateTime => { + Ok(FullType::Basic(t)) + } + Type::BoolArray + | Type::IntArray + | Type::DecimalArray + | Type::StringArray + | Type::DateArray + | Type::DateTimeArray => Ok(FullType::Array(Box::new(FullType::Basic( + t.array().unwrap(), + )))), + other => Err(CompileError::NotABasicType { found: other }), + } + } + + pub fn matches(&self, other: &Type) -> bool { + match self { + FullType::Basic(t1) => t1 == other, + FullType::Array(t1) => t1.simplified().array() == Some(other.clone()), + FullType::Object(_) => other == &Type::Object, + FullType::EnumConstant(_) => false, + } + } + + pub fn simplified(&self) -> Type { + match self { + FullType::Basic(t) => t.clone(), + FullType::Array(t) => t.simplified().array().unwrap_or(Type::ObjectArray), + FullType::Object(_) => Type::Object, + FullType::EnumConstant(_) => Type::String, + } + } + + pub fn object(&self) -> Option> { + match self { + FullType::Object(t) => Some(t.clone()), + _ => None, + } + } + + pub fn array_element_type(&self) -> Option { + match self { + FullType::Basic(t) => match &t { + Type::BoolArray => Some(FullType::Basic(Type::Bool)), + Type::IntArray => Some(FullType::Basic(Type::Int)), + Type::DecimalArray => Some(FullType::Basic(Type::Decimal)), + Type::StringArray => Some(FullType::Basic(Type::String)), + Type::DateArray => Some(FullType::Basic(Type::Date)), + Type::DateTimeArray => Some(FullType::Basic(Type::DateTime)), + Type::ObjectArray => Some(FullType::Basic(Type::Object)), + _ => None, + }, + FullType::Array(t) => Some(*t.clone()), + _ => None, + } + } +} + +pub trait ObjectTypeDefinition { + fn get_field_type(&self, name: &str) -> Option; + + fn get_method_headers(&self, name: &str) -> Option>; + + fn get_static_method(&self, id: &str) -> Option<&dyn StaticMethod>; + + fn get_instance_method(&self, id: &str) -> Option<&dyn InstanceMethod>; +} + +pub struct EmptyRoot; + +impl ObjectTypeDefinition for EmptyRoot { + fn get_field_type(&self, _name: &str) -> Option { + None + } + + fn get_method_headers(&self, _name: &str) -> Option> { + None + } + + fn get_static_method(&self, _id: &str) -> Option<&dyn StaticMethod> { + None + } + + fn get_instance_method(&self, _id: &str) -> Option<&dyn InstanceMethod> { + None + } +} + +pub trait StaticMethod { + fn get_header(&self) -> &MethodHeader; + fn call(&self, args: Vec) -> CompileResult; +} + +pub trait InstanceMethod { + fn get_header(&self) -> &MethodHeader; + fn call(&self, instance: TypedFormula, args: Vec) -> CompileResult; +} + +pub struct MethodHeader { + pub method_id: String, + pub return_type: FullType, + pub argument_types: Vec, +} + +impl MethodHeader { + pub fn new(method_id: String, return_type: Type, argument_types: Vec) -> Self { + Self { + method_id, + return_type: FullType::Basic(return_type), + argument_types: argument_types.into_iter().map(FullType::Basic).collect(), + } + } +} + +pub use compiler::{compile_formula, compile_formula_as}; + +#[cfg(test)] +mod tests; diff --git a/src/compilation/parser.rs b/src/compilation/parser.rs new file mode 100644 index 0000000..e2284e1 --- /dev/null +++ b/src/compilation/parser.rs @@ -0,0 +1,373 @@ +use bigdecimal::BigDecimal; + +use crate::string_utils::unescape_string; + +use super::{ + lexer::{Token, TokenStream}, + CompileError, CompileResult, +}; + +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum BinaryOp { + Plus, + Minus, + Times, + Divide, + Modulo, + Equals, + NotEquals, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual, + Or, + And, + Index, +} + +#[derive(Clone, Copy, Debug)] +pub enum UnaryOp { + Minus, + Not, +} + +pub enum ExpressionTree { + Number(BigDecimal), + Identifier(String), + String(String), + Conditional { + condition: Box, + then: Box, + else_: Box, + }, + UnaryOp { + op: UnaryOp, + value: Box, + }, + BinaryOp { + op: BinaryOp, + left: Box, + right: Box, + }, + GetField { + object: Box, + field: String, + }, + Call { + target: Box, + args: Vec>, + }, +} + +impl ExpressionTree {} + +pub struct ParsedExpression { + pub formula: ExpressionTree, + pub locals: Vec, +} + +pub struct ParsedLocalDefinition { + pub name: String, + pub value: ExpressionTree, +} + +pub fn parse(formula: &str) -> CompileResult { + let mut stream = TokenStream::new(formula); + let formula = parse_expression(&mut stream)?; + let mut locals = Vec::new(); + if stream.maybe(Token::Identifier("where")) { + loop { + let local = parse_local(&mut stream)?; + locals.push(local); + + if !stream.maybe(Token::Comma) { + break; + } + } + } + + if let Some(remaining) = stream.next() { + return Err(CompileError::UnexpectedUnparsedToken { + found: remaining.to_string(), + }); + } + + Ok(ParsedExpression { formula, locals }) +} + +fn parse_local(stream: &mut TokenStream) -> CompileResult { + let name = stream + .next() + .ok_or(CompileError::UnexpectedEndOfInput)? + .require_identifier()?; + stream.expect(Token::Equals)?; + let value = parse_expression(stream)?; + Ok(ParsedLocalDefinition { name, value }) +} + +fn parse_expression(stream: &mut TokenStream) -> CompileResult { + parse_conditional_expression(stream) +} + +fn parse_conditional_expression(stream: &mut TokenStream) -> CompileResult { + if stream.maybe(Token::Identifier("if")) { + let condition = parse_or_expression(stream)?; + stream.expect(Token::Identifier("then"))?; + let then = parse_or_expression(stream)?; + stream.expect(Token::Identifier("else"))?; + let else_ = parse_or_expression(stream)?; + Ok(ExpressionTree::Conditional { + condition: Box::new(condition), + then: Box::new(then), + else_: Box::new(else_), + }) + } else { + parse_or_expression(stream) + } +} + +fn parse_or_expression(stream: &mut TokenStream) -> CompileResult { + let mut left = parse_and_expression(stream)?; + loop { + if stream.maybe(Token::Identifier("or")) { + let right = parse_and_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::Or, + left: Box::new(left), + right: Box::new(right), + }; + } else { + break; + } + } + Ok(left) +} + +fn parse_and_expression(stream: &mut TokenStream) -> CompileResult { + let mut left = parse_compare_expression(stream)?; + loop { + if stream.maybe(Token::Identifier("and")) { + let right = parse_compare_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::And, + left: Box::new(left), + right: Box::new(right), + }; + } else { + break; + } + } + Ok(left) +} + +fn parse_compare_expression(stream: &mut TokenStream) -> CompileResult { + let mut left = parse_additive_expression(stream)?; + loop { + if stream.maybe(Token::Equals) { + let right = parse_additive_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::Equals, + left: Box::new(left), + right: Box::new(right), + }; + } else if stream.maybe(Token::DoubleEquals) { + let right = parse_additive_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::Equals, + left: Box::new(left), + right: Box::new(right), + }; + } else if stream.maybe(Token::NotEquals) { + let right = parse_additive_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::NotEquals, + left: Box::new(left), + right: Box::new(right), + }; + } else if stream.maybe(Token::NotEqualsAlt) { + let right = parse_additive_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::NotEquals, + left: Box::new(left), + right: Box::new(right), + }; + } else if stream.maybe(Token::LessThan) { + let right = parse_additive_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::LessThan, + left: Box::new(left), + right: Box::new(right), + }; + } else if stream.maybe(Token::LessThanOrEqual) { + let right = parse_additive_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::LessThanOrEqual, + left: Box::new(left), + right: Box::new(right), + }; + } else if stream.maybe(Token::GreaterThan) { + let right = parse_additive_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::GreaterThan, + left: Box::new(left), + right: Box::new(right), + }; + } else if stream.maybe(Token::GreaterThanOrEqual) { + let right = parse_additive_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::GreaterThanOrEqual, + left: Box::new(left), + right: Box::new(right), + }; + } else { + break; + } + } + Ok(left) +} + +fn parse_additive_expression(stream: &mut TokenStream) -> CompileResult { + let mut left = parse_multiplicative_expression(stream)?; + loop { + if stream.maybe(Token::Plus) { + let right = parse_multiplicative_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::Plus, + left: Box::new(left), + right: Box::new(right), + }; + } else if stream.maybe(Token::Minus) { + let right = parse_multiplicative_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::Minus, + left: Box::new(left), + right: Box::new(right), + }; + } else { + break; + } + } + Ok(left) +} + +fn parse_multiplicative_expression(stream: &mut TokenStream) -> CompileResult { + let mut left = parse_unary_expression(stream)?; + loop { + if stream.maybe(Token::Times) { + let right = parse_unary_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::Times, + left: Box::new(left), + right: Box::new(right), + }; + } else if stream.maybe(Token::Divide) { + let right = parse_unary_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::Divide, + left: Box::new(left), + right: Box::new(right), + }; + } else if stream.maybe(Token::Modulo) { + let right = parse_unary_expression(stream)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::Modulo, + left: Box::new(left), + right: Box::new(right), + }; + } else { + break; + } + } + Ok(left) +} + +fn parse_unary_expression(stream: &mut TokenStream) -> CompileResult { + if stream.maybe(Token::Minus) { + let right = parse_unary_expression(stream)?; + Ok(ExpressionTree::UnaryOp { + op: UnaryOp::Minus, + value: Box::new(right), + }) + } else if stream.maybe(Token::Identifier("not")) { + let right = parse_unary_expression(stream)?; + Ok(ExpressionTree::UnaryOp { + op: UnaryOp::Not, + value: Box::new(right), + }) + } else { + parse_postfix_expression(stream) + } +} + +fn parse_postfix_expression(stream: &mut TokenStream) -> CompileResult { + let mut left = parse_primary_expression(stream)?; + loop { + if stream.maybe(Token::OpenBracket) { + let index = parse_expression(stream)?; + stream.expect(Token::CloseBracket)?; + left = ExpressionTree::BinaryOp { + op: BinaryOp::Index, + left: Box::new(left), + right: Box::new(index), + }; + } else if stream.maybe(Token::Dot) { + let field = stream + .next() + .ok_or(CompileError::UnexpectedEndOfInput)? + .require_identifier()?; + + left = ExpressionTree::BinaryOp { + op: BinaryOp::Plus, + left: Box::new(left), + right: Box::new(ExpressionTree::Identifier(field)), + }; + } else if stream.maybe(Token::OpenParen) { + let mut args = Vec::new(); + loop { + if stream.maybe(Token::CloseParen) { + break; + } else { + let arg = parse_expression(stream)?; + args.push(Box::new(arg)); + if !stream.maybe(Token::Comma) { + stream.expect(Token::CloseParen)?; + break; + } + } + } + left = ExpressionTree::Call { + target: Box::new(left), + args, + }; + } else { + break; + } + } + Ok(left) +} + +fn parse_primary_expression(stream: &mut TokenStream) -> CompileResult { + if let Some(token) = stream.next() { + match token { + Token::OpenParen => { + let expr = parse_expression(stream)?; + stream.expect(Token::CloseParen)?; + Ok(expr) + } + Token::Number(value) => { + let value = value + .parse() + .map_err(|e| CompileError::CannotParseDecimal(e))?; + Ok(ExpressionTree::Number(value)) + } + Token::String(value) => Ok(ExpressionTree::String(unescape_string(value))), // TODO: Unescape + Token::Identifier(value) => Ok(ExpressionTree::Identifier(value.into())), + _ => Err(CompileError::UnexpectedToken { + expected: "expression".into(), + found: token.to_string(), + }), + } + } else { + Err(CompileError::UnexpectedEndOfInput) + } +} diff --git a/src/compilation/tests.rs b/src/compilation/tests.rs new file mode 100644 index 0000000..50f459d --- /dev/null +++ b/src/compilation/tests.rs @@ -0,0 +1,195 @@ +use std::sync::Arc; + +use crate::{ + compilation::{compile_formula, EmptyRoot}, + value::{EnumValueFormula, EqValue, Value}, + ExecutionError, FormulaObject, +}; + +#[test] +fn test_constant_int() { + let root = EmptyRoot; + let formula = compile_formula("1", &root).unwrap(); + + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(1)); + assert_eq!(formula.format_to_string(), "1".to_string()); + + let serialized = formula.serialize(); + let deserialized = EnumValueFormula::deserialize(&serialized).unwrap(); + let result = deserialized.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(1)); + assert_eq!(deserialized.format_to_string(), "1".to_string()); +} + +#[test] +fn test_constant_string() { + let root = EmptyRoot; + let formula = compile_formula("\"hello\"", &root).unwrap(); + + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).expect("Calculation failed"); + assert_eq!( + result.comparable().unwrap(), + EqValue::String("hello".into()) + ); + assert_eq!(formula.format_to_string(), "\"hello\"".to_string()); + + let serialized = formula.serialize(); + let deserialized = EnumValueFormula::deserialize(&serialized).unwrap(); + let result = deserialized.evaluate(&context).expect("Calculation failed"); + assert_eq!( + result.comparable().unwrap(), + EqValue::String("hello".into()) + ); + assert_eq!(deserialized.format_to_string(), "\"hello\"".to_string()); +} + +#[test] +fn test_add() { + let root = EmptyRoot; + let formula = compile_formula("1 + 2", &root).unwrap(); + + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(3)); + assert_eq!(formula.format_to_string(), "1 + 2".to_string()); + + let serialized = formula.serialize(); + let deserialized = EnumValueFormula::deserialize(&serialized).unwrap(); + let result = deserialized.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(3)); + assert_eq!(deserialized.format_to_string(), "1 + 2".to_string()); +} + +#[test] +fn test_subtract() { + let root = EmptyRoot; + let formula = compile_formula("1 - 2", &root).unwrap(); + + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(-1)); + assert_eq!(formula.format_to_string(), "1 - 2".to_string()); + + let serialized = formula.serialize(); + let deserialized = EnumValueFormula::deserialize(&serialized).unwrap(); + let result = deserialized.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(-1)); + assert_eq!(deserialized.format_to_string(), "1 - 2".to_string()); +} + +#[test] +fn test_multiply() { + let root = EmptyRoot; + let formula = compile_formula("2 * 3", &root).unwrap(); + + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(6)); + assert_eq!(formula.format_to_string(), "2 * 3".to_string()); + + let serialized = formula.serialize(); + let deserialized = EnumValueFormula::deserialize(&serialized).unwrap(); + let result = deserialized.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(6)); + assert_eq!(deserialized.format_to_string(), "2 * 3".to_string()); +} + +#[test] +fn test_divide() { + let root = EmptyRoot; + let formula = compile_formula("6 / 3", &root).unwrap(); + + let context = crate::FormulaContext::new_with_empty_root(); + let result = formula.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(2)); + assert_eq!(formula.format_to_string(), "6 / 3".to_string()); + + let serialized = formula.serialize(); + let deserialized = EnumValueFormula::deserialize(&serialized).unwrap(); + let result = deserialized.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(2)); + assert_eq!(deserialized.format_to_string(), "6 / 3".to_string()); +} + +struct TestRoot { + ivalue: i64, +} + +impl FormulaObject for TestRoot { + fn get(&self, field: &str) -> Result { + match field { + "ivalue" => Ok(Value::Int(self.ivalue)), + _ => Err(ExecutionError::NoSuchField { + typename: "TestRoot".into(), + field: field.into(), + }), + } + } + + fn call(&self, method: &str, _args: Vec) -> Result { + Err(ExecutionError::NoSuchMethod { + typename: "TestRoot".into(), + method: method.into(), + }) + } +} + +struct TestRootDefinition; + +impl crate::compilation::ObjectTypeDefinition for TestRootDefinition { + fn get_field_type(&self, name: &str) -> Option { + match name { + "ivalue" => Some(crate::compilation::FullType::Basic(crate::types::Type::Int)), + _ => None, + } + } + + fn get_method_headers(&self, _name: &str) -> Option> { + None + } + + fn get_static_method(&self, _id: &str) -> Option<&dyn crate::compilation::StaticMethod> { + None + } + + fn get_instance_method(&self, _id: &str) -> Option<&dyn crate::compilation::InstanceMethod> { + None + } +} + +#[test] +fn test_with_context() { + let root = TestRoot { ivalue: 42 }; + let formula = compile_formula("ivalue", &TestRootDefinition).unwrap(); + + let context = crate::FormulaContext::new(Arc::new(root)); + let result = formula.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(42)); + assert_eq!(formula.format_to_string(), "ivalue".to_string()); + + let serialized = formula.serialize(); + let deserialized = EnumValueFormula::deserialize(&serialized).unwrap(); + let result = deserialized.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(42)); + assert_eq!(deserialized.format_to_string(), "ivalue".to_string()); +} + +#[test] +fn test_with_context_mul() { + let root = TestRoot { ivalue: 42 }; + let formula = compile_formula("ivalue * 2", &TestRootDefinition).unwrap(); + + let context = crate::FormulaContext::new(Arc::new(root)); + let result = formula.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(84)); + assert_eq!(formula.format_to_string(), "ivalue * 2".to_string()); + + let serialized = formula.serialize(); + let deserialized = EnumValueFormula::deserialize(&serialized).unwrap(); + let result = deserialized.evaluate(&context).expect("Calculation failed"); + assert_eq!(result.comparable().unwrap(), EqValue::Int(84)); + assert_eq!(deserialized.format_to_string(), "ivalue * 2".to_string()); +} diff --git a/src/compilation/typed_formula.rs b/src/compilation/typed_formula.rs new file mode 100644 index 0000000..94d1e74 --- /dev/null +++ b/src/compilation/typed_formula.rs @@ -0,0 +1,36 @@ +use crate::{ + types::{AnyType, Type}, + value::EnumValueFormula, + BoolFormula, Formula, +}; + +use super::{CompileError, CompileResult, FullType}; + +pub struct TypedFormula { + pub type_: FullType, + pub value: EnumValueFormula, +} + +impl TypedFormula { + pub fn simple(value: EnumValueFormula) -> CompileResult { + let type_ = FullType::from_simple(value.type_())?; + Ok(TypedFormula { type_, value }) + } + + pub fn from(value: Box>) -> CompileResult { + let type_ = FullType::from_simple(T::TYPE)?; + Ok(TypedFormula { + type_, + value: T::to_enum_formula(value), + }) + } + + pub fn require_bool(self) -> CompileResult { + self.value + .to_bool() + .ok_or(CompileError::InvalidArgumentType { + expected: Type::Bool, + found: self.type_.simplified(), + }) + } +} diff --git a/src/date_formula.rs b/src/date_formula.rs new file mode 100644 index 0000000..001d3b7 --- /dev/null +++ b/src/date_formula.rs @@ -0,0 +1,362 @@ +use chrono::{Datelike, Duration, NaiveDate}; + +use crate::formula_reader::FormulaReader; +use crate::formula_string::{FormulaString, OperatorPriority}; +use crate::formula_writer::FormulaWriter; +use crate::int_formula::IntFormulas; +use crate::types::{DateType, Type}; +use crate::value::EnumValueFormula; +use crate::{generic::*, BoolFormula, DateArrayFormula, ObjectFormula}; +use crate::{ + DateFormula, DeserializationError, DeserializedResult, ExecutionError, ExecutionResult, + Formula, FormulaContext, IntFormula, StringFormula, StringFormulas, +}; + +pub struct DateFormulas; + +const DATE_CONSTANT: u8 = 0x10; +const DATE_FROM_ISO_STRING: u8 = 0x11; +const DATE_ADD_DAYS: u8 = 0x12; +const DATE_ADD_MONTHS: u8 = 0x13; +const DATE_ADD_YEARS: u8 = 0x14; + +impl DateFormulas { + pub fn from_bytes(bytes: &[u8]) -> DeserializedResult { + let mut reader = FormulaReader::new(bytes); + Self::from_reader(&mut reader) + } + + pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT => ArrayElement::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT => { + ArrayElementOrDefault::::deserialize(reader) + } + GENERIC_OP_ARRAY_FIRST => ArrayFirst::::deserialize(reader), + GENERIC_OP_ARRAY_LAST => ArrayLast::::deserialize(reader), + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + DATE_CONSTANT => DateConstant::deserialize(reader), + DATE_FROM_ISO_STRING => DateFromIsoString::deserialize(reader), + DATE_ADD_DAYS => DateAddDays::deserialize(reader), + DATE_ADD_MONTHS => DateAddMonths::deserialize(reader), + DATE_ADD_YEARS => DateAddYears::deserialize(reader), + other => Err(DeserializationError::UnknownOperator(Type::Date, other)), + } + } + + pub fn invalid(formula: String) -> DateFormula { + InvalidFormula::::new(formula) + } + + pub fn define_locals(locals: Vec, formula: DateFormula) -> DateFormula { + DefineLocals::::new(locals, formula) + } + + pub fn local(index: u32) -> DateFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> DateFormula { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> DateFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn array_element(array: DateArrayFormula, index: IntFormula) -> DateFormula { + ArrayElement::::new(array, index) + } + + pub fn array_element_or_default( + array: DateArrayFormula, + index: IntFormula, + default: DateFormula, + ) -> DateFormula { + ArrayElementOrDefault::::new(array, index, default) + } + + pub fn array_first(array: DateArrayFormula) -> DateFormula { + ArrayFirst::::new(array) + } + + pub fn array_last(array: DateArrayFormula) -> DateFormula { + ArrayLast::::new(array) + } + + pub fn ternary(condition: BoolFormula, then: DateFormula, else_: DateFormula) -> DateFormula { + Ternary::::new(condition, then, else_) + } + + pub fn value(value: NaiveDate) -> DateFormula { + DateConstant::new(value) + } + + pub fn from_iso_string(value: StringFormula) -> DateFormula { + DateFromIsoString::new(value) + } + + pub fn add_days(date: DateFormula, days: IntFormula) -> DateFormula { + DateAddDays::new(date, days) + } + + pub fn add_months(date: DateFormula, months: IntFormula) -> DateFormula { + DateAddMonths::new(date, months) + } + + pub fn add_years(date: DateFormula, years: IntFormula) -> DateFormula { + DateAddYears::new(date, years) + } +} + +struct DateConstant { + value: NaiveDate, +} + +impl DateConstant { + pub fn new(value: NaiveDate) -> DateFormula { + Box::new(Self { value }) + } + + fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let days = reader.read_i32()?; + let date = NaiveDate::from_num_days_from_ce_opt(days) + .ok_or(DeserializationError::InvalidDate(days))?; + Ok(Self::new(date)) + } +} + +impl Formula for DateConstant { + fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult { + Ok(self.value) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DATE_CONSTANT); + writer.write_i32(self.value.num_days_from_ce()); + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + crate::formula_string::FormulaString::new( + format!("date({})", self.value), + crate::formula_string::OperatorPriority::Literal, + ) + } +} + +struct DateFromIsoString { + value: StringFormula, +} + +impl DateFromIsoString { + pub fn new(value: StringFormula) -> DateFormula { + Box::new(Self { value }) + } + + fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let value = StringFormulas::from_reader(reader)?; + Ok(Self::new(value)) + } +} + +impl Formula for DateFromIsoString { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let value = self.value.evaluate(context)?; + let date = NaiveDate::parse_from_str(&value, "%Y-%m-%d") + .map_err(|_| ExecutionError::InvalidISODate(value))?; + Ok(date) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DATE_FROM_ISO_STRING); + self.value.serialize_to(writer); + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + crate::formula_string::FormulaString::unary( + self.value.as_ref(), + crate::formula_string::OperatorPriority::Function, + |value| format!("date({})", value), + ) + } +} + +struct DateAddDays { + date: DateFormula, + days: IntFormula, +} + +impl DateAddDays { + pub fn new(date: DateFormula, days: IntFormula) -> DateFormula { + Box::new(Self { date, days }) + } + + fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let date = DateFormulas::from_reader(reader)?; + let days = IntFormulas::from_reader(reader)?; + Ok(Self::new(date, days)) + } +} + +impl Formula for DateAddDays { + fn evaluate(&self, context: &FormulaContext) -> Result { + let date = self.date.evaluate(context)?; + let days = self.days.evaluate(context)?; + Ok(date + Duration::days(days)) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DATE_ADD_DAYS); + self.date.serialize_to(writer); + self.days.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let left = self.date.to_formula_string().wrap(OperatorPriority::Member); + let right = self + .days + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("{}.add_days({})", left, right), + OperatorPriority::Member, + ) + } +} + +fn days_in_month(date: &NaiveDate) -> u32 { + let month = date.month(); + match month { + 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, + 4 | 6 | 9 | 11 => 30, + 2 => { + if date.leap_year() { + 29 + } else { + 28 + } + } + _ => panic!("Invalid month: {}", month), + } +} + +struct DateAddMonths { + date: DateFormula, + months: IntFormula, +} + +impl DateAddMonths { + pub fn new(date: DateFormula, months: IntFormula) -> DateFormula { + Box::new(Self { date, months }) + } + + fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let date = DateFormulas::from_reader(reader)?; + let months = IntFormulas::from_reader(reader)?; + Ok(Self::new(date, months)) + } +} + +impl Formula for DateAddMonths { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let date = self.date.evaluate(context)?; + let months = self.months.evaluate(context)?; + let year = date.year(); + let month = date.month(); + let day = date.day(); + let mut year = year + (months / 12) as i32; + let mut month = month + (months % 12) as u32; + if month > 12 { + year += 1; + month -= 12; + } + let date = NaiveDate::from_ymd_opt(year, month, day).unwrap_or_else(|| { + let base_date = NaiveDate::from_ymd_opt(year, month, 1) + .expect("First day of month must always exist"); + NaiveDate::from_ymd_opt(year, month, days_in_month(&base_date)) + .expect("Last day of month must always exist") + }); + Ok(date) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DATE_ADD_MONTHS); + self.date.serialize_to(writer); + self.months.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let left = self.date.to_formula_string().wrap(OperatorPriority::Member); + let right = self + .months + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("{}.add_months({})", left, right), + OperatorPriority::Member, + ) + } +} + +struct DateAddYears { + date: DateFormula, + years: IntFormula, +} + +impl DateAddYears { + pub fn new(date: DateFormula, years: IntFormula) -> DateFormula { + Box::new(Self { date, years }) + } + + fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let date = DateFormulas::from_reader(reader)?; + let years = IntFormulas::from_reader(reader)?; + Ok(Self::new(date, years)) + } +} + +impl Formula for DateAddYears { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let date = self.date.evaluate(context)?; + let years = self.years.evaluate(context)?; + let year = date.year(); + let month = date.month(); + let day = date.day(); + + let date = NaiveDate::from_ymd_opt(year + years as i32, month, day).unwrap_or_else(|| { + let base_date = NaiveDate::from_ymd_opt(year + years as i32, month, 1) + .expect("First day of month must always exist"); + NaiveDate::from_ymd_opt(year + years as i32, month, days_in_month(&base_date)) + .expect("Last day of month must always exist") + }); + Ok(date) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DATE_ADD_YEARS); + self.date.serialize_to(writer); + self.years.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let left = self.date.to_formula_string().wrap(OperatorPriority::Member); + let right = self + .years + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("{}.add_years({})", left, right), + OperatorPriority::Member, + ) + } +} diff --git a/src/datetime_formula.rs b/src/datetime_formula.rs new file mode 100644 index 0000000..90e99da --- /dev/null +++ b/src/datetime_formula.rs @@ -0,0 +1,414 @@ +use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike}; + +use crate::{ + formula_reader::FormulaReader, + formula_string::{FormulaString, OperatorPriority}, + formula_writer::FormulaWriter, + generic::*, + types::{DateTimeType, Type}, + value::EnumValueFormula, + BoolFormula, DateTimeArrayFormula, DateTimeFormula, DeserializationError, DeserializedResult, + ExecutionError, ExecutionResult, Formula, FormulaContext, IntFormula, IntFormulas, + ObjectFormula, StringFormula, StringFormulas, +}; + +pub struct DateTimeFormulas; + +const DATE_TIME_CONSTANT: u8 = 0x10; +const DATE_TIME_FROM_ISO_STRING: u8 = 0x11; +const DATE_WITH_TIME: u8 = 0x12; +const DATE_TIME_SHIFT_TIMEZONE: u8 = 0x13; + +impl DateTimeFormulas { + pub fn from_bytes(bytes: &[u8]) -> DeserializedResult { + let mut reader = FormulaReader::new(bytes); + Self::from_reader(&mut reader) + } + + pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT => ArrayElement::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT => { + ArrayElementOrDefault::::deserialize(reader) + } + GENERIC_OP_ARRAY_FIRST => ArrayFirst::::deserialize(reader), + GENERIC_OP_ARRAY_LAST => ArrayLast::::deserialize(reader), + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + DATE_TIME_CONSTANT => DateTimeConstant::deserialize(reader), + DATE_TIME_FROM_ISO_STRING => DateTimeFromIsoString::deserialize(reader), + DATE_WITH_TIME => DateWithTime::deserialize(reader), + DATE_TIME_SHIFT_TIMEZONE => DateTimeShiftTimezone::deserialize(reader), + other => Err(DeserializationError::UnknownOperator(Type::DateTime, other)), + } + } + + pub fn invalid(formula: String) -> DateTimeFormula { + InvalidFormula::::new(formula) + } + + pub fn define_locals( + locals: Vec, + formula: DateTimeFormula, + ) -> DateTimeFormula { + DefineLocals::::new(locals, formula) + } + + pub fn local(index: u32) -> DateTimeFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> DateTimeFormula { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> DateTimeFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn array_element(array: DateTimeArrayFormula, index: IntFormula) -> DateTimeFormula { + ArrayElement::::new(array, index) + } + + pub fn array_element_or_default( + array: DateTimeArrayFormula, + index: IntFormula, + default: DateTimeFormula, + ) -> DateTimeFormula { + ArrayElementOrDefault::::new(array, index, default) + } + + pub fn array_first(array: DateTimeArrayFormula) -> DateTimeFormula { + ArrayFirst::::new(array) + } + + pub fn array_last(array: DateTimeArrayFormula) -> DateTimeFormula { + ArrayLast::::new(array) + } + + pub fn ternary( + condition: BoolFormula, + then: DateTimeFormula, + else_: DateTimeFormula, + ) -> DateTimeFormula { + Ternary::::new(condition, then, else_) + } + + pub fn value(value: NaiveDateTime) -> DateTimeFormula { + DateTimeConstant::new(value) + } + + pub fn from_iso_string(value: StringFormula) -> DateTimeFormula { + DateTimeFromIsoString::new(value) + } + + pub fn date_with_time( + date: DateTimeFormula, + hours: IntFormula, + minutes: IntFormula, + seconds: IntFormula, + ) -> DateTimeFormula { + DateWithTime::new(date, hours, minutes, seconds) + } +} + +struct DateTimeConstant { + value: NaiveDateTime, +} + +impl DateTimeConstant { + pub fn new(value: NaiveDateTime) -> DateTimeFormula { + Box::new(Self { value }) + } + + fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let day = reader.read_i32()?; + let millis_in_day = reader.read_u32()?; + let date = NaiveDate::from_num_days_from_ce_opt(day) + .ok_or(DeserializationError::InvalidDate(day))?; + let time = NaiveTime::from_num_seconds_from_midnight_opt( + millis_in_day / 1000, + (millis_in_day % 1000) * 1000_000, + ) + .ok_or(DeserializationError::InvalidTime(millis_in_day))?; + let value = date.and_time(time); + Ok(Self::new(value)) + } +} + +impl Formula for DateTimeConstant { + fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult { + Ok(self.value) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DATE_TIME_CONSTANT); + let day = self.value.date().num_days_from_ce(); + let millis_in_day = + self.value.num_seconds_from_midnight() * 1000 + self.value.nanosecond() / 1000_000; + writer.write_i32(day); + writer.write_u32(millis_in_day); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new( + format!( + "DateTime({}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03})", + self.value.year(), + self.value.month(), + self.value.day(), + self.value.hour(), + self.value.minute(), + self.value.second(), + self.value.nanosecond() / 1_000_000 + ), + OperatorPriority::Literal, + ) + } +} + +struct DateTimeFromIsoString { + value: StringFormula, +} + +impl DateTimeFromIsoString { + pub fn new(value: StringFormula) -> DateTimeFormula { + Box::new(Self { value }) + } + + fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let value = StringFormulas::from_reader(reader)?; + Ok(Self::new(value)) + } +} + +impl Formula for DateTimeFromIsoString { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let value = self.value.evaluate(context)?; + let date = NaiveDateTime::parse_from_str(&value, "%Y-%m-%dT%H:%M:%S%.f") + .map_err(|_| ExecutionError::InvalidISODateTime(value))?; + Ok(date) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DATE_TIME_FROM_ISO_STRING); + self.value.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.value.as_ref(), OperatorPriority::Function, |value| { + format!("from_iso({})", value) + }) + } +} + +struct DateWithTime { + date: DateTimeFormula, + hours: IntFormula, + minutes: IntFormula, + seconds: IntFormula, +} + +impl DateWithTime { + pub fn new( + date: DateTimeFormula, + hours: IntFormula, + minutes: IntFormula, + seconds: IntFormula, + ) -> DateTimeFormula { + Box::new(Self { + date, + hours, + minutes, + seconds, + }) + } + + fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let date = DateTimeFormulas::from_reader(reader)?; + let hours = IntFormulas::from_reader(reader)?; + let minutes = IntFormulas::from_reader(reader)?; + let seconds = IntFormulas::from_reader(reader)?; + Ok(Self::new(date, hours, minutes, seconds)) + } +} + +impl Formula for DateWithTime { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let date = self.date.evaluate(context)?; + let hours = self.hours.evaluate(context)?; + let minutes = self.minutes.evaluate(context)?; + let seconds = self.seconds.evaluate(context)?; + Ok(date + .date() + .and_hms_opt(hours as u32, minutes as u32, seconds as u32) + .ok_or(ExecutionError::InvalidDateTime { + year: date.year(), + month: date.month(), + day: date.day(), + hour: hours as u32, + minute: minutes as u32, + second: seconds as u32, + })?) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DATE_WITH_TIME); + self.date.serialize_to(writer); + self.hours.serialize_to(writer); + self.minutes.serialize_to(writer); + self.seconds.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let date = self.date.to_formula_string().wrap(OperatorPriority::Member); + FormulaString::new( + format!( + "{}.with_time({:02}, {:02}, {:02})", + date, + self.hours.to_string(), + self.minutes.to_string(), + self.seconds.to_string() + ), + OperatorPriority::Function, + ) + } +} + +enum FormulaTimeZone { + UTC(chrono::Utc), + FixedOffset(chrono::FixedOffset), + Specified(chrono_tz::Tz), +} + +impl FormulaTimeZone { + fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let kind = reader.read_byte()?; + match kind { + 0 => Ok(Self::UTC(chrono::Utc)), + 1 => { + let offset = reader.read_i32()?; + Ok(Self::FixedOffset( + chrono::FixedOffset::east_opt(offset) + .ok_or(DeserializationError::InvalidTimezoneOffset(offset))?, + )) + } + 2 => { + let timezone = reader.read_string()?; + Ok(Self::Specified(timezone.parse().map_err(|_| { + DeserializationError::InvalidTimezoneName(timezone) + })?)) + } + other => Err(DeserializationError::InvalidTimezoneKind(other)), + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + match self { + Self::UTC(_) => writer.write_byte(0), + Self::FixedOffset(offset) => { + writer.write_byte(1); + writer.write_i32(offset.local_minus_utc()); + } + Self::Specified(timezone) => { + writer.write_byte(2); + writer.write_string(timezone.name()); + } + } + } +} + +struct DateTimeShiftTimezone { + date: DateTimeFormula, + from: FormulaTimeZone, + to: FormulaTimeZone, +} + +impl DateTimeShiftTimezone { + pub fn new( + date: DateTimeFormula, + from: FormulaTimeZone, + to: FormulaTimeZone, + ) -> DateTimeFormula { + Box::new(Self { date, from, to }) + } + + fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let date = DateTimeFormulas::from_reader(reader)?; + let from = FormulaTimeZone::deserialize(reader)?; + let to = FormulaTimeZone::deserialize(reader)?; + Ok(Self::new(date, from, to)) + } +} + +impl Formula for DateTimeShiftTimezone { + fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult { + /*let naive = self.date.evaluate(context)?; + let localized = match &self.from { + FormulaTimeZone::UTC(utc) => match &self.to { + FormulaTimeZone::UTC(_) => naive, + FormulaTimeZone::FixedOffset(offset) => offset.from_utc_datetime(&naive), + FormulaTimeZone::Specified(timezone) => timezone.from_utc_datetime(&naive), + }, + FormulaTimeZone::FixedOffset(offset) => match &self.to { + FormulaTimeZone::UTC(_) => offset.from_local_datetime(&naive).unwrap(), + FormulaTimeZone::FixedOffset(_) => offset.from_local_datetime(&naive).unwrap(), + FormulaTimeZone::Specified(timezone) => timezone.from_local_datetime(&naive), + }, + FormulaTimeZone::Specified(from_timezone) => match &self.to { + FormulaTimeZone::UTC(_) => timezone.from_utc_datetime(&naive), + FormulaTimeZone::FixedOffset(offset) => offset.from_local_datetime(&naive).unwrap(), + FormulaTimeZone::Specified(to_timezone) => { + let local = naive.and_local_timezone(tz); + match local { + LocalResult::Single(local) => to_timezone.from_local_datetime(&local), + LocalResult::Ambiguous(first, second) => { + Err(ExecutionError::TimezoneConversionAmbiguous { + year: naive.year(), + month: naive.month(), + day: naive.day(), + hour: naive.hour(), + minute: naive.minute(), + second: naive.second(), + from_tz: from_timezone.name().into(), + to_tz: to_timezone.name().into(), + })? + } + LocalResult::None => Err(ExecutionError::TimezoneConversionImpossible { + year: naive.year(), + month: naive.month(), + day: naive.day(), + hour: naive.hour(), + minute: naive.minute(), + second: naive.second(), + from_tz: from_timezone.name().into(), + to_tz: to_timezone.name().into(), + })?, + } + } + }, + }; + Ok(localized)*/ + todo!() + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DATE_TIME_SHIFT_TIMEZONE); + self.date.serialize_to(writer); + self.from.serialize_to(writer); + self.to.serialize_to(writer); + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + todo!() + } +} diff --git a/src/decimal_formula.rs b/src/decimal_formula.rs new file mode 100644 index 0000000..00d1b82 --- /dev/null +++ b/src/decimal_formula.rs @@ -0,0 +1,1132 @@ +use std::num::NonZeroU64; +use std::str::FromStr; + +use bigdecimal::num_bigint::BigInt; +use bigdecimal::{BigDecimal, Context, One, RoundingMode, Zero}; + +use crate::array_formula::DecimalArrayFormulas; +use crate::formula_reader::FormulaReader; +use crate::formula_string::{FormulaString, OperatorPriority}; +use crate::formula_writer::FormulaWriter; +use crate::types::{DecimalType, Type}; +use crate::value::EnumValueFormula; +use crate::{generic::*, ObjectFormula}; +use crate::{ + BoolFormula, DecimalArrayFormula, DecimalFormula, DeserializationError, DeserializedResult, + ExecutionError, ExecutionResult, Formula, FormulaContext, IntFormula, IntFormulas, + StringFormula, StringFormulas, +}; + +const DECIMAL_OP_ZERO: u8 = 0x10; +const DECIMAL_OP_ONE: u8 = 0x11; +const DECIMAL_OP_VALUE: u8 = 0x12; +const DECIMAL_NEGATE: u8 = 0x13; +const DECIMAL_ADD: u8 = 0x14; +const DECIMAL_SUBTRACT: u8 = 0x15; +const DECIMAL_MULTIPLY: u8 = 0x16; +const DECIMAL_DIVIDE: u8 = 0x17; +const DECIMAL_ABS: u8 = 0x18; +const DECIMAL_MIN: u8 = 0x19; +const DECIMAL_MAX: u8 = 0x1A; +const DECIMAL_P10: u8 = 0x1B; +const DECIMAL_ROUND: u8 = 0x1C; +const DECIMAL_SQRT: u8 = 0x1D; +const DECIMAL_FROM_INT: u8 = 0x1E; +const DECIMAL_FROM_STRING: u8 = 0x1F; +const DECIMAL_ARRAY_MIN: u8 = 0x21; +const DECIMAL_ARRAY_MAX: u8 = 0x22; +const DECIMAL_ARRAY_SUM: u8 = 0x23; +const DECIMAL_ARRAY_AVG: u8 = 0x24; +const DECIMAL_ARRAY_MEDIAN: u8 = 0x25; + +const ROUNDING_DOWN: u8 = 0; +const ROUNDING_UP: u8 = 1; +const ROUNDING_FLOOR: u8 = 2; +const ROUNDING_CEILING: u8 = 3; +const ROUNDING_HALF_UP: u8 = 4; +const ROUNDING_HALF_DOWN: u8 = 5; +const ROUNDING_HALF_EVEN: u8 = 6; + +pub struct DecimalFormulas; + +impl DecimalFormulas { + pub fn from_bytes(bytes: &[u8]) -> DeserializedResult { + let mut reader = FormulaReader::new(bytes); + Self::from_reader(&mut reader) + } + + pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT => ArrayElement::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT => { + ArrayElementOrDefault::::deserialize(reader) + } + GENERIC_OP_ARRAY_FIRST => ArrayFirst::::deserialize(reader), + GENERIC_OP_ARRAY_LAST => ArrayLast::::deserialize(reader), + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + DECIMAL_OP_ZERO => Ok(DecimalZero::new()), + DECIMAL_OP_ONE => Ok(DecimalOne::new()), + DECIMAL_OP_VALUE => DecimalValue::deserialize(reader), + DECIMAL_NEGATE => DecimalNegate::deserialize(reader), + DECIMAL_ADD => DecimalAdd::deserialize(reader), + DECIMAL_SUBTRACT => DecimalSubtract::deserialize(reader), + DECIMAL_MULTIPLY => DecimalMultiply::deserialize(reader), + DECIMAL_DIVIDE => DecimalDivide::deserialize(reader), + DECIMAL_ABS => DecimalAbs::deserialize(reader), + DECIMAL_MIN => DecimalMin::deserialize(reader), + DECIMAL_MAX => DecimalMax::deserialize(reader), + DECIMAL_P10 => DecimalP10::deserialize(reader), + DECIMAL_ROUND => DecimalRound::deserialize(reader), + DECIMAL_SQRT => DecimalSqrt::deserialize(reader), + DECIMAL_FROM_INT => DecimalFromInt::deserialize(reader), + DECIMAL_FROM_STRING => DecimalFromString::deserialize(reader), + DECIMAL_ARRAY_MIN => DecimalArrayMin::deserialize(reader), + DECIMAL_ARRAY_MAX => DecimalArrayMax::deserialize(reader), + DECIMAL_ARRAY_SUM => DecimalArraySum::deserialize(reader), + DECIMAL_ARRAY_AVG => DecimalArrayAvg::deserialize(reader), + DECIMAL_ARRAY_MEDIAN => DecimalArrayMedian::deserialize(reader), + other => Err(DeserializationError::UnknownOperator(Type::Decimal, other)), + } + } + + pub fn invalid(formula: String) -> DecimalFormula { + InvalidFormula::::new(formula) + } + + pub fn define_locals(locals: Vec, body: DecimalFormula) -> DecimalFormula { + DefineLocals::::new(locals, body) + } + + pub fn local(index: u32) -> DecimalFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> DecimalFormula { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> DecimalFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn array_element(array: DecimalArrayFormula, index: IntFormula) -> DecimalFormula { + ArrayElement::::new(array, index) + } + + pub fn array_element_or_default( + array: DecimalArrayFormula, + index: IntFormula, + default: DecimalFormula, + ) -> DecimalFormula { + ArrayElementOrDefault::::new(array, index, default) + } + + pub fn array_first(array: DecimalArrayFormula) -> DecimalFormula { + ArrayFirst::::new(array) + } + + pub fn array_last(array: DecimalArrayFormula) -> DecimalFormula { + ArrayLast::::new(array) + } + + pub fn ternary( + condition: BoolFormula, + if_true: DecimalFormula, + if_false: DecimalFormula, + ) -> DecimalFormula { + Ternary::::new(condition, if_true, if_false) + } + + pub fn zero() -> DecimalFormula { + DecimalZero::new() + } + + pub fn one() -> DecimalFormula { + DecimalOne::new() + } + + pub fn value(value: BigDecimal) -> DecimalFormula { + DecimalValue::new(value) + } + + pub fn negate(operand: DecimalFormula) -> DecimalFormula { + DecimalNegate::new(operand) + } + + pub fn add(left: DecimalFormula, right: DecimalFormula) -> DecimalFormula { + DecimalAdd::new(left, right) + } + + pub fn subtract(left: DecimalFormula, right: DecimalFormula) -> DecimalFormula { + DecimalSubtract::new(left, right) + } + + pub fn multiply(left: DecimalFormula, right: DecimalFormula) -> DecimalFormula { + DecimalMultiply::new(left, right) + } + + pub fn divide( + left: DecimalFormula, + right: DecimalFormula, + precision: NonZeroU64, + rounding_mode: RoundingMode, + ) -> DecimalFormula { + DecimalDivide::new(left, right, precision, rounding_mode) + } + + pub fn abs(operand: DecimalFormula) -> DecimalFormula { + DecimalAbs::new(operand) + } + + pub fn min(left: DecimalFormula, right: DecimalFormula) -> DecimalFormula { + DecimalMin::new(left, right) + } + + pub fn max(left: DecimalFormula, right: DecimalFormula) -> DecimalFormula { + DecimalMax::new(left, right) + } + + pub fn multiply_p10(operand: DecimalFormula, shift: i64) -> DecimalFormula { + DecimalP10::new(operand, shift) + } + + pub fn round(operand: DecimalFormula, digits: i64, mode: RoundingMode) -> DecimalFormula { + DecimalRound::new(operand, digits, mode) + } + + pub fn sqrt( + operand: DecimalFormula, + precision: NonZeroU64, + mode: RoundingMode, + ) -> DecimalFormula { + DecimalSqrt::new(operand, precision, mode) + } + + pub fn from_int(operand: IntFormula) -> DecimalFormula { + DecimalFromInt::new(operand) + } + + pub fn from_string(operand: StringFormula) -> DecimalFormula { + DecimalFromString::new(operand) + } + + pub fn array_min(operand: DecimalArrayFormula) -> DecimalFormula { + DecimalArrayMin::new(operand) + } + + pub fn array_max(operand: DecimalArrayFormula) -> DecimalFormula { + DecimalArrayMax::new(operand) + } + + pub fn array_sum(operand: DecimalArrayFormula) -> DecimalFormula { + DecimalArraySum::new(operand) + } + + pub fn array_avg(operand: DecimalArrayFormula) -> DecimalFormula { + DecimalArrayAvg::new(operand) + } + + pub fn array_median(operand: DecimalArrayFormula) -> DecimalFormula { + DecimalArrayMedian::new(operand) + } +} + +fn decode_rounding_mode(mode: u8) -> Result { + match mode { + ROUNDING_DOWN => Ok(RoundingMode::Down), + ROUNDING_UP => Ok(RoundingMode::Up), + ROUNDING_FLOOR => Ok(RoundingMode::Floor), + ROUNDING_CEILING => Ok(RoundingMode::Ceiling), + ROUNDING_HALF_UP => Ok(RoundingMode::HalfUp), + ROUNDING_HALF_DOWN => Ok(RoundingMode::HalfDown), + ROUNDING_HALF_EVEN => Ok(RoundingMode::HalfEven), + _ => Err(DeserializationError::InvalidRoundingMode(mode)), + } +} + +struct DecimalZero {} + +impl DecimalZero { + pub fn new() -> DecimalFormula { + Box::new(Self {}) + } +} + +impl Formula for DecimalZero { + fn evaluate(&self, _context: &FormulaContext) -> Result { + Ok(BigDecimal::zero()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_OP_ZERO); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new("0".to_string(), OperatorPriority::Literal) + } +} + +struct DecimalOne {} + +impl DecimalOne { + pub fn new() -> DecimalFormula { + Box::new(Self {}) + } +} + +impl Formula for DecimalOne { + fn evaluate(&self, _context: &FormulaContext) -> Result { + Ok(BigDecimal::one()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_OP_ONE); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new("1".to_string(), OperatorPriority::Literal) + } +} + +struct DecimalValue { + value: BigDecimal, +} + +impl DecimalValue { + pub fn new(value: BigDecimal) -> DecimalFormula { + Box::new(Self { value }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let bytes = reader.read_bytes()?; + let int_value = BigInt::from_signed_bytes_be(&bytes); + let scale = reader.read_i64()?; + let value = BigDecimal::new(int_value.into(), scale); + Ok(Self::new(value)) + } +} + +impl Formula for DecimalValue { + fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult { + Ok(self.value.clone()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_OP_VALUE); + let (int_value, scale) = self.value.clone().into_bigint_and_exponent(); + let bytes = int_value.to_signed_bytes_be(); + writer.write_bytes(&bytes); + writer.write_i64(scale); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new(self.value.to_string(), OperatorPriority::Literal) + } +} + +struct DecimalNegate { + operand: DecimalFormula, +} + +impl DecimalNegate { + pub fn new(operand: DecimalFormula) -> DecimalFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for DecimalNegate { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + Ok(-operand) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_NEGATE); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.operand.as_ref(), OperatorPriority::Unary, |operand| { + format!("-{}", operand) + }) + } +} + +struct DecimalAdd { + left: DecimalFormula, + right: DecimalFormula, +} + +impl DecimalAdd { + pub fn new(left: DecimalFormula, right: DecimalFormula) -> DecimalFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DecimalFormulas::from_reader(reader)?; + let right = DecimalFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for DecimalAdd { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left + right) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_ADD); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Sum, + |left, right| format!("{} + {}", left, right), + ) + } +} + +struct DecimalSubtract { + left: DecimalFormula, + right: DecimalFormula, +} + +impl DecimalSubtract { + pub fn new(left: DecimalFormula, right: DecimalFormula) -> DecimalFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DecimalFormulas::from_reader(reader)?; + let right = DecimalFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for DecimalSubtract { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left - right) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_SUBTRACT); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Sum, + |left, right| format!("{} - {}", left, right), + ) + } +} + +struct DecimalMultiply { + left: DecimalFormula, + right: DecimalFormula, +} + +impl DecimalMultiply { + pub fn new(left: DecimalFormula, right: DecimalFormula) -> DecimalFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DecimalFormulas::from_reader(reader)?; + let right = DecimalFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for DecimalMultiply { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left * right) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_MULTIPLY); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Product, + |left, right| format!("{} * {}", left, right), + ) + } +} + +struct DecimalDivide { + left: DecimalFormula, + right: DecimalFormula, + precision: NonZeroU64, + rounding_mode: RoundingMode, +} + +impl DecimalDivide { + pub fn new( + left: DecimalFormula, + right: DecimalFormula, + precision: NonZeroU64, + rounding_mode: RoundingMode, + ) -> DecimalFormula { + Box::new(Self { + left, + right, + precision, + rounding_mode, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DecimalFormulas::from_reader(reader)?; + let right = DecimalFormulas::from_reader(reader)?; + let precision = reader.read_u32()?; + let precision = NonZeroU64::try_from(precision as u64) + .map_err(|_| DeserializationError::InvalidPrecision(precision))?; + let rounding_mode = decode_rounding_mode(reader.read_byte()?)?; + Ok(Self::new(left, right, precision, rounding_mode)) + } +} + +impl Formula for DecimalDivide { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + if right.is_zero() { + Err(ExecutionError::DivideByZero) + } else { + let result = left / right; + Ok(result.with_precision_round(self.precision, self.rounding_mode)) + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_DIVIDE); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + writer.write_u32(self.precision.get() as u32); + writer.write_byte(match self.rounding_mode { + RoundingMode::Down => ROUNDING_DOWN, + RoundingMode::Up => ROUNDING_UP, + RoundingMode::Floor => ROUNDING_FLOOR, + RoundingMode::Ceiling => ROUNDING_CEILING, + RoundingMode::HalfUp => ROUNDING_HALF_UP, + RoundingMode::HalfDown => ROUNDING_HALF_DOWN, + RoundingMode::HalfEven => ROUNDING_HALF_EVEN, + }); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Product, + |left, right| format!("{} / {}", left, right), + ) + } +} + +struct DecimalAbs { + operand: DecimalFormula, +} + +impl DecimalAbs { + pub fn new(operand: DecimalFormula) -> DecimalFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for DecimalAbs { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + Ok(operand.abs()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_ABS); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let value = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new(format!("abs({})", value), OperatorPriority::Function) + } +} + +struct DecimalMin { + left: DecimalFormula, + right: DecimalFormula, +} + +impl DecimalMin { + pub fn new(left: DecimalFormula, right: DecimalFormula) -> DecimalFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DecimalFormulas::from_reader(reader)?; + let right = DecimalFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for DecimalMin { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left.min(right)) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_MIN); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let left = self + .left + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + let right = self + .right + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("min({}, {})", left, right), + OperatorPriority::Function, + ) + } +} + +struct DecimalMax { + left: DecimalFormula, + right: DecimalFormula, +} + +impl DecimalMax { + pub fn new(left: DecimalFormula, right: DecimalFormula) -> DecimalFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DecimalFormulas::from_reader(reader)?; + let right = DecimalFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for DecimalMax { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left.max(right)) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_MAX); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let left = self + .left + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + let right = self + .right + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("max({}, {})", left, right), + OperatorPriority::Function, + ) + } +} + +struct DecimalP10 { + operand: DecimalFormula, + shift: i64, +} + +impl DecimalP10 { + pub fn new(operand: DecimalFormula, shift: i64) -> DecimalFormula { + Box::new(Self { operand, shift }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalFormulas::from_reader(reader)?; + let shift = reader.read_i64()?; + Ok(Self::new(operand, shift)) + } +} + +impl Formula for DecimalP10 { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + let shift = self.shift; + let (int_val, scale) = operand.into_bigint_and_exponent(); + Ok(BigDecimal::new(int_val, scale + shift)) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_P10); + self.operand.serialize_to(writer); + writer.write_i64(self.shift); + } + + fn to_formula_string(&self) -> FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("{}.shift({})", operand, self.shift), + OperatorPriority::Member, + ) + } +} + +struct DecimalRound { + operand: DecimalFormula, + digits: i64, + mode: RoundingMode, +} + +impl DecimalRound { + pub fn new(operand: DecimalFormula, digits: i64, mode: RoundingMode) -> DecimalFormula { + Box::new(Self { + operand, + digits, + mode, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalFormulas::from_reader(reader)?; + let digits = reader.read_i64()?; + let mode = decode_rounding_mode(reader.read_byte()?)?; + Ok(Self::new(operand, digits, mode)) + } +} + +impl Formula for DecimalRound { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + Ok(operand.with_scale_round(self.digits, self.mode)) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_ROUND); + self.operand.serialize_to(writer); + writer.write_i64(self.digits); + writer.write_byte(match self.mode { + RoundingMode::Down => ROUNDING_DOWN, + RoundingMode::Up => ROUNDING_UP, + RoundingMode::Floor => ROUNDING_FLOOR, + RoundingMode::Ceiling => ROUNDING_CEILING, + RoundingMode::HalfUp => ROUNDING_HALF_UP, + RoundingMode::HalfDown => ROUNDING_HALF_DOWN, + RoundingMode::HalfEven => ROUNDING_HALF_EVEN, + }); + } + + fn to_formula_string(&self) -> FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("round({}, {}, {:?})", operand, self.digits, self.mode), + OperatorPriority::Function, + ) + } +} + +struct DecimalSqrt { + operand: DecimalFormula, + precision: NonZeroU64, + mode: RoundingMode, +} + +impl DecimalSqrt { + pub fn new( + operand: DecimalFormula, + precision: NonZeroU64, + mode: RoundingMode, + ) -> DecimalFormula { + Box::new(Self { + operand, + precision, + mode, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalFormulas::from_reader(reader)?; + let precision = reader.read_u32()?; + let precision = NonZeroU64::try_from(precision as u64) + .map_err(|_| DeserializationError::InvalidPrecision(precision))?; + let mode = decode_rounding_mode(reader.read_byte()?)?; + Ok(Self::new(operand, precision, mode)) + } +} + +impl Formula for DecimalSqrt { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + let context = Context::new(self.precision, self.mode); + let result = operand + .sqrt_with_context(&context) + .ok_or(ExecutionError::CannotTakeSqrtOfNegativeDecimal(operand))?; + Ok(result) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_SQRT); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("sqrt({}, {:?}, {:?})", operand, self.precision, self.mode), + OperatorPriority::Function, + ) + } +} + +struct DecimalFromInt { + operand: IntFormula, +} + +impl DecimalFromInt { + pub fn new(operand: IntFormula) -> DecimalFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = IntFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for DecimalFromInt { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + Ok(BigDecimal::from(operand)) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_FROM_INT); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new(format!("decimal({})", operand), OperatorPriority::Function) + } +} + +struct DecimalFromString { + operand: StringFormula, +} + +impl DecimalFromString { + pub fn new(operand: StringFormula) -> DecimalFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = StringFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for DecimalFromString { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + let value = BigDecimal::from_str(&operand) + .map_err(|_| ExecutionError::CannotParseToDecimal(operand))?; + Ok(value) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_FROM_STRING); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new(format!("decimal({})", operand), OperatorPriority::Function) + } +} + +struct DecimalArrayMin { + operand: DecimalArrayFormula, +} + +impl DecimalArrayMin { + pub fn new(operand: DecimalArrayFormula) -> DecimalFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalArrayFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for DecimalArrayMin { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + let mut min = None; + for value in operand { + if let Some(ref min_value) = min { + if value < *min_value { + min = Some(value); + } + } else { + min = Some(value); + } + } + Ok(min.map(|x| x.clone()).unwrap_or_else(|| BigDecimal::zero())) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_ARRAY_MIN); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new(format!("min({})", operand), OperatorPriority::Function) + } +} + +struct DecimalArrayMax { + operand: DecimalArrayFormula, +} + +impl DecimalArrayMax { + pub fn new(operand: DecimalArrayFormula) -> DecimalFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalArrayFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for DecimalArrayMax { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + let mut max = None; + for value in operand { + if let Some(ref max_value) = max { + if value > *max_value { + max = Some(value); + } + } else { + max = Some(value); + } + } + Ok(max.unwrap_or_else(|| BigDecimal::zero())) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_ARRAY_MAX); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new(format!("max({})", operand), OperatorPriority::Function) + } +} + +struct DecimalArraySum { + operand: DecimalArrayFormula, +} + +impl DecimalArraySum { + pub fn new(operand: DecimalArrayFormula) -> DecimalFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalArrayFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for DecimalArraySum { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + let mut sum = BigDecimal::zero(); + for value in operand { + sum += value; + } + Ok(sum) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_ARRAY_SUM); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new(format!("sum({})", operand), OperatorPriority::Function) + } +} + +struct DecimalArrayAvg { + operand: DecimalArrayFormula, +} + +impl DecimalArrayAvg { + pub fn new(operand: DecimalArrayFormula) -> DecimalFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalArrayFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for DecimalArrayAvg { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + let mut sum = BigDecimal::zero(); + let mut count = 0; + for value in operand { + sum += value; + count += 1; + } + if count == 0 { + Ok(BigDecimal::zero()) + } else { + Ok(sum / BigDecimal::from(count)) + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_ARRAY_AVG); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new(format!("avg({})", operand), OperatorPriority::Function) + } +} + +struct DecimalArrayMedian { + operand: DecimalArrayFormula, +} + +impl DecimalArrayMedian { + pub fn new(operand: DecimalArrayFormula) -> DecimalFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalArrayFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for DecimalArrayMedian { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let mut values = self.operand.evaluate(context)?; + values.sort_unstable(); + let count = values.len(); + if count == 0 { + Ok(BigDecimal::zero()) + } else if count % 2 == 0 { + let left = &values[count / 2 - 1]; + let right = &values[count / 2]; + Ok((left + right) / BigDecimal::from(2)) + } else { + Ok(values[count / 2].clone()) + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(DECIMAL_ARRAY_MEDIAN); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new(format!("median({})", operand), OperatorPriority::Function) + } +} diff --git a/src/formula_reader.rs b/src/formula_reader.rs new file mode 100644 index 0000000..e393508 --- /dev/null +++ b/src/formula_reader.rs @@ -0,0 +1,88 @@ +use crate::{DeserializationError, DeserializedResult}; + +pub struct FormulaReader<'a> { + bytes: &'a [u8], + pos: usize, +} + +impl<'a> FormulaReader<'a> { + pub fn new(bytes: &'a [u8]) -> FormulaReader<'a> { + FormulaReader { bytes, pos: 0 } + } + + pub fn read_byte(&mut self) -> DeserializedResult { + if self.pos >= self.bytes.len() { + return Err(DeserializationError::UnexpectedEndOfData); + } + let byte = self.bytes[self.pos]; + self.pos += 1; + Ok(byte) + } + + pub fn read_u32(&mut self) -> DeserializedResult { + let mut result: u32 = 0; + let mut shift = 0; + loop { + let byte: u32 = self.read_byte()?.into(); + result |= (byte & 0x7f) << shift; + if byte & 0x80 == 0 { + break; + } + shift += 7; + } + Ok(result) + } + + pub fn read_i32(&mut self) -> DeserializedResult { + let unsigned = self.read_u32()?; + let mut result = (unsigned >> 1) as i32; + if unsigned & 1 != 0 { + result = !result; + } + Ok(result) + } + + pub fn read_u64(&mut self) -> DeserializedResult { + let mut result: u64 = 0; + let mut shift = 0; + loop { + let byte: u64 = self.read_byte()?.into(); + result |= (byte & 0x7f) << shift; + if byte & 0x80 == 0 { + break; + } + shift += 7; + } + Ok(result) + } + + pub fn read_i64(&mut self) -> DeserializedResult { + let unsigned = self.read_u64()?; + let mut result = (unsigned >> 1) as i64; + if unsigned & 1 != 0 { + result = !result; + } + Ok(result) + } + + pub fn read_bytes(&mut self) -> DeserializedResult<&'a [u8]> { + let length = self.read_u32()? as usize; + if self.pos + length > self.bytes.len() { + return Err(DeserializationError::UnexpectedEndOfData); + } + let result = &self.bytes[self.pos..self.pos + length]; + self.pos += length; + Ok(result) + } + + pub fn read_string(&mut self) -> DeserializedResult { + let bytes = self.read_bytes()?; + String::from_utf8(bytes.to_vec()).map_err(|_| DeserializationError::InvalidUtf8) + } + + pub fn read_char(&mut self) -> DeserializedResult { + let separator = + char::try_from(self.read_u32()?).map_err(|_| DeserializationError::InvalidCharacter)?; + Ok(separator) + } +} diff --git a/src/formula_string.rs b/src/formula_string.rs new file mode 100644 index 0000000..93291a4 --- /dev/null +++ b/src/formula_string.rs @@ -0,0 +1,77 @@ +use crate::Formula; + +pub struct FormulaString { + pub value: String, + priority: OperatorPriority, +} + +impl FormulaString { + pub fn new(value: String, priority: OperatorPriority) -> Self { + Self { value, priority } + } + + pub fn unary String>( + operand: &dyn Formula, + priority: OperatorPriority, + wrap: F, + ) -> Self { + let operand = operand.to_formula_string(); + Self::new(wrap(operand.wrap(priority)), priority) + } + + pub fn binary String>( + left: &dyn Formula, + right: &dyn Formula, + priority: OperatorPriority, + wrap: F, + ) -> Self { + let left = left.to_formula_string(); + let right = right.to_formula_string(); + Self::new(wrap(left.wrap(priority), right.wrap(priority)), priority) + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum OperatorPriority { + None, + And, + Or, + Comparison, + Sum, + Product, + Power, + Unary, + Function, + Member, + Parentheses, + Literal, +} + +impl OperatorPriority { + fn relative_priority(&self) -> i32 { + match self { + OperatorPriority::None => 0, + OperatorPriority::And => 1, + OperatorPriority::Or => 2, + OperatorPriority::Comparison => 3, + OperatorPriority::Sum => 4, + OperatorPriority::Product => 5, + OperatorPriority::Power => 6, + OperatorPriority::Unary => 7, + OperatorPriority::Function => 8, + OperatorPriority::Member => 9, + OperatorPriority::Parentheses => 10, + OperatorPriority::Literal => 11, + } + } +} + +impl FormulaString { + pub fn wrap(self, to_priority: OperatorPriority) -> String { + if self.priority.relative_priority() < to_priority.relative_priority() { + format!("({})", self.value) + } else { + self.value + } + } +} diff --git a/src/formula_writer.rs b/src/formula_writer.rs new file mode 100644 index 0000000..0368d86 --- /dev/null +++ b/src/formula_writer.rs @@ -0,0 +1,75 @@ +pub struct FormulaWriter { + bytes: Vec, +} + +impl FormulaWriter { + pub fn new() -> Self { + FormulaWriter { bytes: Vec::new() } + } + + pub fn write_byte(&mut self, byte: u8) { + self.bytes.push(byte); + } + + pub fn write_u32(&mut self, mut value: u32) { + loop { + let byte = (value & 0x7f) as u8; + value >>= 7; + if value == 0 { + self.write_byte(byte); + break; + } else { + self.write_byte(byte | 0x80); + } + } + } + + pub fn write_i32(&mut self, value: i32) { + let unsigned = (value as u32) << 1; + if value < 0 { + self.write_u32(!unsigned | 1); + } else { + self.write_u32(unsigned); + } + } + + pub fn write_u64(&mut self, mut value: u64) { + loop { + let byte = (value & 0x7f) as u8; + value >>= 7; + if value == 0 { + self.write_byte(byte); + break; + } else { + self.write_byte(byte | 0x80); + } + } + } + + pub fn write_i64(&mut self, value: i64) { + let unsigned = (value as u64) << 1; + if value < 0 { + self.write_u64(!unsigned | 1); + } else { + self.write_u64(unsigned); + } + } + + pub fn write_bytes(&mut self, bytes: &[u8]) { + self.write_u32(bytes.len() as u32); + self.bytes.extend_from_slice(bytes); + } + + pub fn write_string(&mut self, string: &str) { + self.write_bytes(string.as_bytes()); + } + + pub fn write_char(&mut self, char: char) { + let value = char as u32; + self.write_u32(value); + } + + pub fn into_bytes(self) -> Vec { + self.bytes + } +} diff --git a/src/generic.rs b/src/generic.rs new file mode 100644 index 0000000..5e9b506 --- /dev/null +++ b/src/generic.rs @@ -0,0 +1,539 @@ +use crate::{ + formula_reader::FormulaReader, + formula_string::{FormulaString, OperatorPriority}, + formula_writer::FormulaWriter, + types::{AnyType, ScalarType}, + value::{ + deserialize_arguments_from_reader, deserialize_value_formula_from_reader, EnumValueFormula, + Value, + }, + BoolFormula, BoolFormulas, DeserializedResult, ExecutionError, ExecutionResult, Formula, + FormulaContext, IntFormula, IntFormulas, ObjectFormula, ObjectFormulas, +}; + +pub const GENERIC_OP_INVALID: u8 = 0x01; +pub const GENERIC_OP_DEFINE_LOCALS: u8 = 0x02; +pub const GENERIC_OP_LOCAL: u8 = 0x03; +pub const GENERIC_OP_OBJECT_FIELD: u8 = 0x04; +pub const GENERIC_OP_OBJECT_METHOD_CALL: u8 = 0x05; +pub const GENERIC_OP_ARRAY_ELEMENT: u8 = 0x06; +pub const GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT: u8 = 0x07; +pub const GENERIC_OP_ARRAY_FIRST: u8 = 0x08; +pub const GENERIC_OP_ARRAY_LAST: u8 = 0x09; +pub const GENERIC_OP_TERNARY: u8 = 0x0a; + +pub struct InvalidFormula { + content: String, + phantom: std::marker::PhantomData, +} + +impl InvalidFormula { + pub fn new(content: String) -> Box> { + Box::new(Self { + content, + phantom: std::marker::PhantomData, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult>> { + let content = reader.read_string()?; + Ok(Self::new(content)) + } +} + +impl Formula for InvalidFormula { + fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult { + Err(ExecutionError::InvalidFormula(self.content.clone())) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(GENERIC_OP_INVALID); + writer.write_string(&self.content); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new("invalid".to_string(), OperatorPriority::Literal) + } +} + +pub struct LocalDefinition { + name: String, + value: EnumValueFormula, +} + +impl LocalDefinition { + pub fn new(name: String, value: EnumValueFormula) -> Self { + Self { name, value } + } +} + +pub struct DefineLocals { + locals: Vec, + value: Box>, +} + +impl DefineLocals { + pub fn new( + locals: Vec, + value: Box>, + ) -> Box> { + Box::new(Self { locals, value }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult>> { + let locals = reader.read_u32()?; + let mut definitions = Vec::with_capacity(locals as usize); + for _ in 0..locals { + let name = reader.read_string()?; + let value = deserialize_value_formula_from_reader(reader)?; + definitions.push(LocalDefinition { name, value }); + } + let value = T::formula_from_reader(reader)?; + Ok(Self::new(definitions, value)) + } +} + +impl Formula for DefineLocals { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let mut local_context = context.clone(); + for definition in &self.locals { + let value = definition.value.evaluate(&local_context)?; + local_context.locals.push(value); + } + self.value.evaluate(&local_context) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(GENERIC_OP_DEFINE_LOCALS); + writer.write_u32(self.locals.len() as u32); + for definition in &self.locals { + writer.write_string(&definition.name); + definition.value.serialize_to(writer); + } + self.value.serialize_to(writer); + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + let result = self.value.to_formula_string().value; + let variables = self + .locals + .iter() + .map(|local| format!("{} = {}", local.name, local.value.format_to_string())) + .collect::>() + .join(", "); + FormulaString::new( + format!("{} where {}", result, variables), + OperatorPriority::None, + ) + } +} + +pub struct LocalVariable { + local: u32, + phantom: std::marker::PhantomData, +} + +impl LocalVariable { + pub fn new(local: u32) -> Box> { + Box::new(Self { + local, + phantom: std::marker::PhantomData, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult>> { + let local = reader.read_u32()?; + Ok(Self::new(local)) + } +} + +impl Formula for LocalVariable { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let local = context + .get_local(self.local as usize) + .ok_or(ExecutionError::NoSuchLocal(self.local as usize))?; + T::cast_from_value(local).ok_or(ExecutionError::NotA(T::TYPE)) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(GENERIC_OP_LOCAL); + writer.write_u32(self.local); + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + todo!() + } +} + +pub struct ObjectField { + object: ObjectFormula, + field: String, + phantom: std::marker::PhantomData, +} + +impl ObjectField { + pub fn new(object: ObjectFormula, field: String) -> Box> { + Box::new(Self { + object, + field, + phantom: std::marker::PhantomData, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult>> { + let object = ObjectFormulas::from_reader(reader)?; + let field = reader.read_string()?; + Ok(Self::new(object, field)) + } +} + +impl Formula for ObjectField { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let object = self.object.evaluate(context)?; + let value = object.get(&self.field)?; + T::cast_from_value(value).ok_or(ExecutionError::NotA(T::TYPE)) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(GENERIC_OP_OBJECT_FIELD); + self.object.serialize_to(writer); + writer.write_string(&self.field); + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + if self.object.is_root() { + return FormulaString::new(self.field.clone(), OperatorPriority::Member); + } + + FormulaString::new( + format!("{}.{}", self.object.to_formula_string().value, self.field), + OperatorPriority::Member, + ) + } +} + +pub struct ObjectMethodCall { + object: ObjectFormula, + method: String, + arguments: Vec, + phantom: std::marker::PhantomData, +} + +impl ObjectMethodCall { + pub fn new( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> Box> { + Box::new(Self { + object, + method, + arguments, + phantom: std::marker::PhantomData, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult>> { + let object = ObjectFormulas::from_reader(reader)?; + let method = reader.read_string()?; + let arguments = deserialize_arguments_from_reader(reader)?; + Ok(Self::new(object, method, arguments)) + } +} + +impl Formula for ObjectMethodCall { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let object = self.object.evaluate(context)?; + let arguments = evaluate_arguments(&self.arguments, context)?; + let value = object.call(&self.method, arguments)?; + T::cast_from_value(value).ok_or(ExecutionError::NotA(T::TYPE)) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(GENERIC_OP_OBJECT_METHOD_CALL); + self.object.serialize_to(writer); + writer.write_string(&self.method); + writer.write_u32(self.arguments.len() as u32); + for argument in &self.arguments { + argument.serialize_to(writer); + } + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + let arguments = self + .arguments + .iter() + .map(|argument| argument.format_to_string()) + .collect::>() + .join(", "); + + if self.object.is_root() { + FormulaString::new( + format!("{}({})", self.method, arguments), + OperatorPriority::Function, + ) + } else { + FormulaString::new( + format!( + "{}.{}({})", + self.object.to_formula_string().value, + self.method, + arguments + ), + OperatorPriority::Function, + ) + } + } +} + +pub struct ArrayElement { + array: Box>>, + index: IntFormula, +} + +impl ArrayElement { + pub fn new(array: Box>>, index: IntFormula) -> Box> { + Box::new(Self { array, index }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult>> { + let array = T::ARRAY::formula_from_reader(reader)?; + let index = IntFormulas::from_reader(reader)?; + Ok(Box::new(Self { array, index })) + } +} + +impl Formula for ArrayElement { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let array = self.array.evaluate(context)?; + let index = self.index.evaluate(context)?; + if index < 0 || (index as usize) >= array.len() { + Err(ExecutionError::IndexOutOfBounds(index as usize)) + } else { + Ok(array[index as usize].clone()) + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(GENERIC_OP_ARRAY_ELEMENT); + self.array.serialize_to(writer); + self.index.serialize_to(writer); + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + FormulaString::binary( + self.array.as_ref(), + self.index.as_ref(), + OperatorPriority::Member, + |array, index| format!("{}[{}]", array, index), + ) + } +} + +pub struct ArrayElementOrDefault { + array: Box>>, + index: IntFormula, + default: Box>, +} + +impl ArrayElementOrDefault { + pub fn new( + array: Box>>, + index: IntFormula, + default: Box>, + ) -> Box> { + Box::new(Self { + array, + index, + default, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult>> { + let array = T::ARRAY::formula_from_reader(reader)?; + let index = IntFormulas::from_reader(reader)?; + let default = T::formula_from_reader(reader)?; + Ok(Self::new(array, index, default)) + } +} + +impl Formula for ArrayElementOrDefault { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let array = self.array.evaluate(context)?; + let index = self.index.evaluate(context)?; + if index < 0 || (index as usize) >= array.len() { + self.default.evaluate(context) + } else { + Ok(array[index as usize].clone()) + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT); + self.array.serialize_to(writer); + self.index.serialize_to(writer); + self.default.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new( + format!( + "{}.get({}, {})", + self.array + .to_formula_string() + .wrap(OperatorPriority::Member), + self.index + .to_formula_string() + .wrap(OperatorPriority::Parentheses), + self.default + .to_formula_string() + .wrap(OperatorPriority::Parentheses) + ), + OperatorPriority::Member, + ) + } +} + +pub struct ArrayFirst { + array: Box>>, +} + +impl ArrayFirst { + pub fn new(array: Box>>) -> Box> { + Box::new(Self { array }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult>> { + let array = T::ARRAY::formula_from_reader(reader)?; + Ok(Box::new(Self { array })) + } +} + +impl Formula for ArrayFirst { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let array = self.array.evaluate(context)?; + if array.is_empty() { + Err(ExecutionError::EmptyArray) + } else { + Ok(array[0].clone()) + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(GENERIC_OP_ARRAY_FIRST); + self.array.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new( + format!("{}.first", self.array.to_formula_string().value), + OperatorPriority::Member, + ) + } +} + +pub struct ArrayLast { + array: Box>>, +} + +impl ArrayLast { + pub fn new(array: Box>>) -> Box> { + Box::new(Self { array }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult>> { + let array = T::ARRAY::formula_from_reader(reader)?; + Ok(Box::new(Self { array })) + } +} + +impl Formula for ArrayLast { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let array = self.array.evaluate(context)?; + if array.is_empty() { + Err(ExecutionError::EmptyArray) + } else { + Ok(array[array.len() - 1].clone()) + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(GENERIC_OP_ARRAY_LAST); + self.array.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new( + format!("{}.last", self.array.to_formula_string().value), + OperatorPriority::Member, + ) + } +} + +pub struct Ternary { + condition: BoolFormula, + true_value: Box>, + false_value: Box>, +} + +impl Ternary { + pub fn new( + condition: BoolFormula, + true_value: Box>, + false_value: Box>, + ) -> Box> { + Box::new(Self { + condition, + true_value, + false_value, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult>> { + let condition = BoolFormulas::from_reader(reader)?; + let true_value = T::formula_from_reader(reader)?; + let false_value = T::formula_from_reader(reader)?; + Ok(Self::new(condition, true_value, false_value)) + } +} + +impl Formula for Ternary { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let condition = self.condition.evaluate(context)?; + if condition { + self.true_value.evaluate(context) + } else { + self.false_value.evaluate(context) + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(GENERIC_OP_TERNARY); + self.condition.serialize_to(writer); + self.true_value.serialize_to(writer); + self.false_value.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new( + format!( + "if {} then {} else {}", + self.condition.to_formula_string().value, + self.true_value.to_formula_string().value, + self.false_value.to_formula_string().value + ), + OperatorPriority::None, + ) + } +} + +fn evaluate_arguments( + arguments: &[EnumValueFormula], + context: &FormulaContext, +) -> ExecutionResult> { + let mut result = Vec::with_capacity(arguments.len()); + for argument in arguments { + result.push(argument.evaluate(context)?); + } + Ok(result) +} diff --git a/src/int_formula.rs b/src/int_formula.rs new file mode 100644 index 0000000..9f0faf0 --- /dev/null +++ b/src/int_formula.rs @@ -0,0 +1,1698 @@ +use bigdecimal::ToPrimitive; +use chrono::Datelike; + +use crate::formula_reader::FormulaReader; +use crate::formula_string::{FormulaString, OperatorPriority}; +use crate::value::{EnumValueFormula, Value}; +use crate::{ + generic::*, BoolArrayFormula, DateArrayFormula, DateTimeArrayFormula, DateTimeFormula, + DateTimeFormulas, DecimalArrayFormula, ObjectFormula, StringArrayFormula, +}; +use crate::{ + types::*, BoolFormula, BoolFormulas, DecimalFormula, DecimalFormulas, IntArrayFormula, + IntArrayFormulas, ObjectArrayFormula, ObjectArrayFormulas, +}; +use crate::{ + DateFormula, DateFormulas, DeserializationError, DeserializedResult, ExecutionError, + ExecutionResult, Formula, FormulaContext, IntFormula, StringFormula, StringFormulas, +}; + +const INT_OP_ZERO: u8 = 0x10; +const INT_OP_ONE: u8 = 0x11; +const INT_OP_VALUE: u8 = 0x12; +const INT_NEGATE: u8 = 0x13; +const INT_ADD: u8 = 0x14; +const INT_SUBTRACT: u8 = 0x15; +const INT_MULTIPLY: u8 = 0x16; +const INT_DIVIDE: u8 = 0x17; +const INT_MODULO: u8 = 0x18; +const INT_ABS: u8 = 0x19; +const INT_SQRT: u8 = 0x1A; +const INT_FLOOR: u8 = 0x1B; +const INT_CEIL: u8 = 0x1C; +const INT_MIN: u8 = 0x1D; +const INT_MAX: u8 = 0x1E; +const INT_FROM_STRING: u8 = 0x1F; +const INT_DAYS_BETWEEN: u8 = 0x20; +const INT_MONTHS_BETWEEN: u8 = 0x21; +const INT_YEARS_BETWEEN: u8 = 0x22; +const INT_BOOLARRAY_LENGTH: u8 = 0x23; +const INT_INTARRAY_LENGTH: u8 = 0x24; +const INT_DECIMALARRAY_LENGTH: u8 = 0x25; +const INT_STRINGARRAY_LENGTH: u8 = 0x26; +const INT_DATEARRAY_LENGTH: u8 = 0x27; +const INT_DATETIMEARRAY_LENGTH: u8 = 0x28; +const INT_OBJECTARRAY_LENGTH: u8 = 0x29; +const INT_OBJECTARRAY_FIND_INDEX: u8 = 0x2A; +const INT_INTARRAY_MIN: u8 = 0x2B; +const INT_INTARRAY_MAX: u8 = 0x2C; +const INT_INTARRAY_SUM: u8 = 0x2D; +const INT_INTARRAY_AVG: u8 = 0x2E; +const INT_INTARRAY_MEDIAN: u8 = 0x2F; +const INT_INTARRAY_COUNT: u8 = 0x30; +const INT_DECIMALARRAY_COUNT: u8 = 0x31; +const INT_STRINGARRAY_COUNT: u8 = 0x32; +const INT_DATEARRAY_COUNT: u8 = 0x33; +const INT_DATETIMEARRAY_COUNT: u8 = 0x34; +const INT_OBJECTARRAY_COUNT: u8 = 0x35; +const INT_DAYS_BETWEEN_DT: u8 = 0x36; +const INT_MONTHS_BETWEEN_DT: u8 = 0x37; +const INT_YEARS_BETWEEN_DT: u8 = 0x38; +const INT_HOURS_BETWEEN: u8 = 0x39; +const INT_MINUTES_BETWEEN: u8 = 0x3A; +const INT_SECONDS_BETWEEN: u8 = 0x3B; +const INT_MILLISECONDS_BETWEEN: u8 = 0x3C; + +pub struct IntFormulas {} + +impl IntFormulas { + pub fn from_bytes(bytes: &[u8]) -> DeserializedResult { + let mut reader = FormulaReader::new(bytes); + Self::from_reader(&mut reader) + } + + pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult { + deserialize_int_formula_from_reader(reader) + } + + pub fn invalid(formula: String) -> IntFormula { + InvalidFormula::::new(formula) + } + + pub fn define_locals(locals: Vec, formula: IntFormula) -> IntFormula { + DefineLocals::::new(locals, formula) + } + + pub fn local(index: u32) -> IntFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> IntFormula { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> IntFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn array_element(array: IntArrayFormula, index: IntFormula) -> IntFormula { + ArrayElement::::new(array, index) + } + + pub fn array_element_or_default( + array: IntArrayFormula, + index: IntFormula, + default: IntFormula, + ) -> IntFormula { + ArrayElementOrDefault::::new(array, index, default) + } + + pub fn array_first(array: IntArrayFormula) -> IntFormula { + ArrayFirst::::new(array) + } + + pub fn array_last(array: IntArrayFormula) -> IntFormula { + ArrayLast::::new(array) + } + + pub fn ternary( + condition: BoolFormula, + if_true: IntFormula, + if_false: IntFormula, + ) -> IntFormula { + Ternary::::new(condition, if_true, if_false) + } + + pub fn zero() -> IntFormula { + IntZero::new() + } + + pub fn one() -> IntFormula { + IntOne::new() + } + + pub fn value(value: i64) -> IntFormula { + IntValue::new(value) + } + + pub fn negate(operand: IntFormula) -> IntFormula { + IntNegate::new(operand) + } + + pub fn add(left: IntFormula, right: IntFormula) -> IntFormula { + IntAdd::new(left, right) + } + + pub fn subtract(left: IntFormula, right: IntFormula) -> IntFormula { + IntSubtract::new(left, right) + } + + pub fn multiply(left: IntFormula, right: IntFormula) -> IntFormula { + IntMultiply::new(left, right) + } + + pub fn divide(left: IntFormula, right: IntFormula) -> IntFormula { + IntDivide::new(left, right) + } + + pub fn modulo(left: IntFormula, right: IntFormula) -> IntFormula { + IntModulo::new(left, right) + } + + pub fn abs(operand: IntFormula) -> IntFormula { + IntAbs::new(operand) + } + + pub fn sqrt(operand: IntFormula) -> IntFormula { + IntSqrt::new(operand) + } + + pub fn floor(operand: DecimalFormula) -> IntFormula { + IntFloor::new(operand) + } + + pub fn ceil(operand: DecimalFormula) -> IntFormula { + IntCeil::new(operand) + } + + pub fn min(left: IntFormula, right: IntFormula) -> IntFormula { + IntMin::new(left, right) + } + + pub fn max(left: IntFormula, right: IntFormula) -> IntFormula { + IntMax::new(left, right) + } + + pub fn from_string(operand: StringFormula) -> IntFormula { + IntFromString::new(operand) + } + + pub fn days_between(left: DateFormula, right: DateFormula) -> IntFormula { + IntDaysBetween::new(left, right) + } + + pub fn months_between(left: DateFormula, right: DateFormula) -> IntFormula { + IntMonthsBetween::new(left, right) + } + + pub fn years_between(left: DateFormula, right: DateFormula) -> IntFormula { + IntYearsBetween::new(left, right) + } + + pub fn days_between_dt(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + IntDaysBetweenDT::new(left, right) + } + + pub fn months_between_dt(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + IntMonthsBetweenDT::new(left, right) + } + + pub fn years_between_dt(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + IntYearsBetweenDT::new(left, right) + } + + pub fn hours_between(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + IntHoursBetween::new(left, right) + } + + pub fn minutes_between(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + IntMinutesBetween::new(left, right) + } + + pub fn seconds_between(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + IntSecondsBetween::new(left, right) + } + + pub fn milliseconds_between(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + IntMillisecondsBetween::new(left, right) + } + + pub fn boolarray_length(array: BoolArrayFormula) -> IntFormula { + ArrayLength::::new(array) + } + + pub fn intarray_length(array: IntArrayFormula) -> IntFormula { + ArrayLength::::new(array) + } + + pub fn decimalarray_length(array: DecimalArrayFormula) -> IntFormula { + ArrayLength::::new(array) + } + + pub fn stringarray_length(array: StringArrayFormula) -> IntFormula { + ArrayLength::::new(array) + } + + pub fn datearray_length(array: DateArrayFormula) -> IntFormula { + ArrayLength::::new(array) + } + + pub fn datetimearray_length(array: DateTimeArrayFormula) -> IntFormula { + ArrayLength::::new(array) + } + + pub fn objectarray_length(array: ObjectArrayFormula) -> IntFormula { + ArrayLength::::new(array) + } + + pub fn objectarray_find_index(array: ObjectArrayFormula, predicate: BoolFormula) -> IntFormula { + ObjectArrayFindIndex::new(array, predicate) + } + + pub fn intarray_min(array: IntArrayFormula) -> IntFormula { + IntArrayMin::new(array) + } + + pub fn intarray_max(array: IntArrayFormula) -> IntFormula { + IntArrayMax::new(array) + } + + pub fn intarray_sum(array: IntArrayFormula) -> IntFormula { + IntArraySum::new(array) + } + + pub fn intarray_avg(array: IntArrayFormula) -> IntFormula { + IntArrayAvg::new(array) + } + + pub fn intarray_median(array: IntArrayFormula) -> IntFormula { + IntArrayMedian::new(array) + } + + pub fn intarray_count(array: IntArrayFormula, predicate: BoolFormula) -> IntFormula { + ArrayCount::::new(array, predicate) + } + + pub fn decimalarray_count(array: DecimalArrayFormula, predicate: BoolFormula) -> IntFormula { + ArrayCount::::new(array, predicate) + } + + pub fn stringarray_count(array: StringArrayFormula, predicate: BoolFormula) -> IntFormula { + ArrayCount::::new(array, predicate) + } + + pub fn datearray_count(array: DateArrayFormula, predicate: BoolFormula) -> IntFormula { + ArrayCount::::new(array, predicate) + } + + pub fn datetimearray_count(array: DateTimeArrayFormula, predicate: BoolFormula) -> IntFormula { + ArrayCount::::new(array, predicate) + } + + pub fn objectarray_count(array: ObjectArrayFormula, predicate: BoolFormula) -> IntFormula { + ArrayCount::::new(array, predicate) + } +} + +fn deserialize_int_formula_from_reader( + reader: &mut FormulaReader, +) -> DeserializedResult { + let operand = reader.read_byte()?; + match operand { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT => ArrayElement::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT => { + ArrayElementOrDefault::::deserialize(reader) + } + GENERIC_OP_ARRAY_FIRST => ArrayFirst::::deserialize(reader), + GENERIC_OP_ARRAY_LAST => ArrayLast::::deserialize(reader), + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + INT_OP_ZERO => Ok(IntZero::new()), + INT_OP_ONE => Ok(IntOne::new()), + INT_OP_VALUE => IntValue::deserialize(reader), + INT_NEGATE => IntNegate::deserialize(reader), + INT_ADD => IntAdd::deserialize(reader), + INT_SUBTRACT => IntSubtract::deserialize(reader), + INT_MULTIPLY => IntMultiply::deserialize(reader), + INT_DIVIDE => IntDivide::deserialize(reader), + INT_MODULO => IntModulo::deserialize(reader), + INT_ABS => IntAbs::deserialize(reader), + INT_SQRT => IntSqrt::deserialize(reader), + INT_FLOOR => IntFloor::deserialize(reader), + INT_CEIL => IntCeil::deserialize(reader), + INT_MIN => IntMin::deserialize(reader), + INT_MAX => IntMax::deserialize(reader), + INT_FROM_STRING => IntFromString::deserialize(reader), + INT_MILLISECONDS_BETWEEN => IntMillisecondsBetween::deserialize(reader), + INT_SECONDS_BETWEEN => IntSecondsBetween::deserialize(reader), + INT_MINUTES_BETWEEN => IntMinutesBetween::deserialize(reader), + INT_HOURS_BETWEEN => IntHoursBetween::deserialize(reader), + INT_DAYS_BETWEEN => IntDaysBetween::deserialize(reader), + INT_MONTHS_BETWEEN => IntMonthsBetween::deserialize(reader), + INT_YEARS_BETWEEN => IntYearsBetween::deserialize(reader), + INT_DAYS_BETWEEN_DT => IntDaysBetweenDT::deserialize(reader), + INT_MONTHS_BETWEEN_DT => IntMonthsBetweenDT::deserialize(reader), + INT_YEARS_BETWEEN_DT => IntYearsBetweenDT::deserialize(reader), + INT_BOOLARRAY_LENGTH => ArrayLength::::deserialize(reader), + INT_INTARRAY_LENGTH => ArrayLength::::deserialize(reader), + INT_DECIMALARRAY_LENGTH => ArrayLength::::deserialize(reader), + INT_STRINGARRAY_LENGTH => ArrayLength::::deserialize(reader), + INT_DATEARRAY_LENGTH => ArrayLength::::deserialize(reader), + INT_DATETIMEARRAY_LENGTH => ArrayLength::::deserialize(reader), + INT_OBJECTARRAY_LENGTH => ArrayLength::::deserialize(reader), + INT_OBJECTARRAY_FIND_INDEX => ObjectArrayFindIndex::deserialize(reader), + INT_INTARRAY_MIN => IntArrayMin::deserialize(reader), + INT_INTARRAY_MAX => IntArrayMax::deserialize(reader), + INT_INTARRAY_SUM => IntArraySum::deserialize(reader), + INT_INTARRAY_AVG => IntArrayAvg::deserialize(reader), + INT_INTARRAY_MEDIAN => IntArrayMedian::deserialize(reader), + INT_INTARRAY_COUNT => ArrayCount::::deserialize(reader), + INT_DECIMALARRAY_COUNT => ArrayCount::::deserialize(reader), + INT_STRINGARRAY_COUNT => ArrayCount::::deserialize(reader), + INT_DATEARRAY_COUNT => ArrayCount::::deserialize(reader), + INT_DATETIMEARRAY_COUNT => ArrayCount::::deserialize(reader), + INT_OBJECTARRAY_COUNT => ArrayCount::::deserialize(reader), + other => Err(DeserializationError::UnknownOperator(Type::Int, other)), + } +} + +struct IntZero {} + +impl IntZero { + pub fn new() -> IntFormula { + Box::new(Self {}) + } +} + +impl Formula for IntZero { + fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult { + Ok(0) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_OP_ZERO); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new("0".to_string(), OperatorPriority::Literal) + } +} + +struct IntOne {} + +impl IntOne { + pub fn new() -> IntFormula { + Box::new(Self {}) + } +} + +impl Formula for IntOne { + fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult { + Ok(1) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_OP_ONE); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new("1".to_string(), OperatorPriority::Literal) + } +} + +struct IntValue { + value: i64, +} + +impl IntValue { + pub fn new(value: i64) -> IntFormula { + Box::new(Self { value }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let value = reader.read_i64()?; + Ok(Self::new(value)) + } +} + +impl Formula for IntValue { + fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult { + Ok(self.value) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_OP_VALUE); + writer.write_i64(self.value); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new(self.value.to_string(), OperatorPriority::Literal) + } +} + +struct IntNegate { + operand: IntFormula, +} + +impl IntNegate { + pub fn new(operand: IntFormula) -> IntFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = IntFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for IntNegate { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + Ok(-operand) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_NEGATE); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.operand.as_ref(), OperatorPriority::Unary, |operand| { + format!("-{}", operand) + }) + } +} + +struct IntAdd { + left: IntFormula, + right: IntFormula, +} + +impl IntAdd { + pub fn new(left: IntFormula, right: IntFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = IntFormulas::from_reader(reader)?; + let right = IntFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntAdd { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left + right) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_ADD); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Sum, + |left, right| format!("{} + {}", left, right), + ) + } +} + +struct IntSubtract { + left: Box>, + right: Box>, +} + +impl IntSubtract { + pub fn new(left: Box>, right: Box>) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = IntFormulas::from_reader(reader)?; + let right = IntFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntSubtract { + fn evaluate(&self, context: &FormulaContext) -> Result { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left - right) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_SUBTRACT); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Sum, + |left, right| format!("{} - {}", left, right), + ) + } +} + +struct IntMultiply { + left: IntFormula, + right: IntFormula, +} + +impl IntMultiply { + pub fn new(left: IntFormula, right: IntFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = IntFormulas::from_reader(reader)?; + let right = IntFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntMultiply { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left * right) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_MULTIPLY); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Product, + |left, right| format!("{} * {}", left, right), + ) + } +} + +struct IntDivide { + left: IntFormula, + right: IntFormula, +} + +impl IntDivide { + pub fn new(left: IntFormula, right: IntFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = IntFormulas::from_reader(reader)?; + let right = IntFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntDivide { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + if right == 0 { + return Err(ExecutionError::DivideByZero); + } + Ok(left / right) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_DIVIDE); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Product, + |left, right| format!("{} / {}", left, right), + ) + } +} + +struct IntModulo { + left: IntFormula, + right: IntFormula, +} + +impl IntModulo { + pub fn new(left: IntFormula, right: IntFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = IntFormulas::from_reader(reader)?; + let right = IntFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntModulo { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + if right == 0 { + return Err(ExecutionError::DivideByZero); + } + Ok(left % right) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_MODULO); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Product, + |left, right| format!("{} % {}", left, right), + ) + } +} + +struct IntAbs { + operand: IntFormula, +} + +impl IntAbs { + pub fn new(operand: IntFormula) -> IntFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = IntFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for IntAbs { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + Ok(operand.abs()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_ABS); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.operand.as_ref(), OperatorPriority::Unary, |operand| { + format!("abs({})", operand) + }) + } +} + +struct IntSqrt { + operand: IntFormula, +} + +impl IntSqrt { + pub fn new(operand: IntFormula) -> IntFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = IntFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for IntSqrt { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + if operand < 0 { + return Err(ExecutionError::CannotTakeSqrtOfNegativeInt(operand)); + } + Ok((operand as f64).sqrt() as i64) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_SQRT); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.operand.as_ref(), OperatorPriority::Unary, |operand| { + format!("sqrt({})", operand) + }) + } +} + +struct IntFloor { + operand: DecimalFormula, +} + +impl IntFloor { + pub fn new(operand: DecimalFormula) -> IntFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for IntFloor { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + operand + .to_i64() + .ok_or(ExecutionError::DecimalTooLargeForInt(operand)) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_FLOOR); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.operand.as_ref(), OperatorPriority::Unary, |operand| { + format!("floor({})", operand) + }) + } +} + +struct IntCeil { + operand: DecimalFormula, +} + +impl IntCeil { + pub fn new(operand: DecimalFormula) -> IntFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for IntCeil { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + operand + .with_scale_round(0, bigdecimal::RoundingMode::Ceiling) + .to_i64() + .ok_or(ExecutionError::DecimalTooLargeForInt(operand)) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_CEIL); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.operand.as_ref(), OperatorPriority::Unary, |operand| { + format!("ceil({})", operand) + }) + } +} + +struct IntMin { + left: IntFormula, + right: IntFormula, +} + +impl IntMin { + pub fn new(left: IntFormula, right: IntFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = IntFormulas::from_reader(reader)?; + let right = IntFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntMin { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left.min(right)) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_MIN); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Comparison, + |left, right| format!("min({}, {})", left, right), + ) + } +} + +struct IntMax { + left: IntFormula, + right: IntFormula, +} + +impl IntMax { + pub fn new(left: IntFormula, right: IntFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = IntFormulas::from_reader(reader)?; + let right = IntFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntMax { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left.max(right)) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_MAX); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Comparison, + |left, right| format!("max({}, {})", left, right), + ) + } +} + +struct IntFromString { + operand: StringFormula, +} + +impl IntFromString { + pub fn new(operand: StringFormula) -> IntFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = StringFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for IntFromString { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + match operand.parse::() { + Ok(value) => Ok(value), + Err(_) => Err(ExecutionError::CannotParseToInt(operand)), + } + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_FROM_STRING); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.operand.as_ref(), OperatorPriority::Unary, |operand| { + format!("int({})", operand) + }) + } +} + +struct IntDaysBetween { + left: DateFormula, + right: DateFormula, +} + +impl IntDaysBetween { + pub fn new(left: DateFormula, right: DateFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DateFormulas::from_reader(reader)?; + let right = DateFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntDaysBetween { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok((left - right).num_days()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_DAYS_BETWEEN); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Function, + |left, right| format!("days_between({}, {})", left, right), + ) + } +} + +struct IntMonthsBetween { + left: DateFormula, + right: DateFormula, +} + +impl IntMonthsBetween { + pub fn new(left: DateFormula, right: DateFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DateFormulas::from_reader(reader)?; + let right = DateFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntMonthsBetween { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + let left_year = left.year(); + let left_month = left.month() as i32; + let right_year = right.year(); + let right_month = right.month() as i32; + Ok(((left_year - right_year) * 12 + (left_month - right_month)).into()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_MONTHS_BETWEEN); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Function, + |left, right| format!("months_between({}, {})", left, right), + ) + } +} + +struct IntYearsBetween { + left: DateFormula, + right: DateFormula, +} + +impl IntYearsBetween { + pub fn new(left: DateFormula, right: DateFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DateFormulas::from_reader(reader)?; + let right = DateFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntYearsBetween { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + let left_year = left.year(); + let right_year = right.year(); + Ok((left_year - right_year).into()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_YEARS_BETWEEN); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Function, + |left, right| format!("years_between({}, {})", left, right), + ) + } +} + +struct IntDaysBetweenDT { + left: DateTimeFormula, + right: DateTimeFormula, +} + +impl IntDaysBetweenDT { + pub fn new(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DateTimeFormulas::from_reader(reader)?; + let right = DateTimeFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntDaysBetweenDT { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok((left - right).num_days()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_DAYS_BETWEEN_DT); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Function, + |left, right| format!("days_between({}, {})", left, right), + ) + } +} + +struct IntMonthsBetweenDT { + left: DateTimeFormula, + right: DateTimeFormula, +} + +impl IntMonthsBetweenDT { + pub fn new(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DateTimeFormulas::from_reader(reader)?; + let right = DateTimeFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntMonthsBetweenDT { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + let left_year = left.year(); + let left_month = left.month() as i32; + let right_year = right.year(); + let right_month = right.month() as i32; + Ok(((left_year - right_year) * 12 + (left_month - right_month)).into()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_MONTHS_BETWEEN_DT); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Function, + |left, right| format!("months_between({}, {})", left, right), + ) + } +} + +struct IntYearsBetweenDT { + left: DateTimeFormula, + right: DateTimeFormula, +} + +impl IntYearsBetweenDT { + pub fn new(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DateTimeFormulas::from_reader(reader)?; + let right = DateTimeFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntYearsBetweenDT { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + let left_year = left.year(); + let right_year = right.year(); + Ok((left_year - right_year).into()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_YEARS_BETWEEN_DT); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Function, + |left, right| format!("years_between({}, {})", left, right), + ) + } +} + +struct IntHoursBetween { + left: DateTimeFormula, + right: DateTimeFormula, +} + +impl IntHoursBetween { + pub fn new(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DateTimeFormulas::from_reader(reader)?; + let right = DateTimeFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntHoursBetween { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok((left - right).num_hours()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_HOURS_BETWEEN); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Function, + |left, right| format!("hours_between({}, {})", left, right), + ) + } +} + +struct IntMinutesBetween { + left: DateTimeFormula, + right: DateTimeFormula, +} + +impl IntMinutesBetween { + pub fn new(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DateTimeFormulas::from_reader(reader)?; + let right = DateTimeFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntMinutesBetween { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok((left - right).num_minutes()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_MINUTES_BETWEEN); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Function, + |left, right| format!("minutes_between({}, {})", left, right), + ) + } +} + +struct IntSecondsBetween { + left: DateTimeFormula, + right: DateTimeFormula, +} + +impl IntSecondsBetween { + pub fn new(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DateTimeFormulas::from_reader(reader)?; + let right = DateTimeFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntSecondsBetween { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok((left - right).num_seconds()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_SECONDS_BETWEEN); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Function, + |left, right| format!("seconds_between({}, {})", left, right), + ) + } +} + +struct IntMillisecondsBetween { + left: DateTimeFormula, + right: DateTimeFormula, +} + +impl IntMillisecondsBetween { + pub fn new(left: DateTimeFormula, right: DateTimeFormula) -> IntFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = DateTimeFormulas::from_reader(reader)?; + let right = DateTimeFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for IntMillisecondsBetween { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok((left - right).num_milliseconds()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_MILLISECONDS_BETWEEN); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Function, + |left, right| format!("millis_between({}, {})", left, right), + ) + } +} + +struct ArrayLength { + array: Box>>, +} + +impl ArrayLength { + pub fn new(array: Box>>) -> IntFormula { + Box::new(Self { array }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let array = T::ARRAY::formula_from_reader(reader)?; + Ok(Self::new(array)) + } +} + +impl Formula for ArrayLength { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let array = self.array.evaluate(context)?; + Ok(array.len() as i64) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + let op = match T::TYPE { + Type::BoolArray => INT_BOOLARRAY_LENGTH, + Type::IntArray => INT_INTARRAY_LENGTH, + Type::DecimalArray => INT_DECIMALARRAY_LENGTH, + Type::StringArray => INT_STRINGARRAY_LENGTH, + Type::DateArray => INT_DATEARRAY_LENGTH, + Type::DateTimeArray => INT_DATETIMEARRAY_LENGTH, + Type::ObjectArray => INT_OBJECTARRAY_LENGTH, + _ => panic!("Invalid array type"), + }; + writer.write_byte(op); + self.array.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.array.as_ref(), OperatorPriority::Unary, |array| { + format!("{}.length", array) + }) + } +} + +struct ObjectArrayFindIndex { + array: ObjectArrayFormula, + predicate: BoolFormula, +} + +impl ObjectArrayFindIndex { + pub fn new(array: ObjectArrayFormula, predicate: BoolFormula) -> IntFormula { + Box::new(Self { array, predicate }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let array = ObjectArrayFormulas::from_reader(reader)?; + let predicate = BoolFormulas::from_reader(reader)?; + Ok(Self::new(array, predicate)) + } +} + +impl Formula for ObjectArrayFindIndex { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let array = self.array.evaluate(context)?; + let mut local_context = context.clone(); + let index = array + .iter() + .enumerate() + .position(|(_index, item)| { + local_context.locals.push(Value::Object(item.clone())); + let result = self.predicate.evaluate(&local_context).unwrap_or_default(); + local_context.locals.pop(); + result + }) + .map(|x| x as i64) + .unwrap_or(-1); + Ok(index) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_OBJECTARRAY_FIND_INDEX); + self.array.serialize_to(writer); + self.predicate.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.array.as_ref(), + self.predicate.as_ref(), + OperatorPriority::Function, + |array, predicate| format!("{}.find_index({})", array, predicate), + ) + } +} + +struct IntArrayMin { + array: IntArrayFormula, +} + +impl IntArrayMin { + pub fn new(array: IntArrayFormula) -> IntFormula { + Box::new(Self { array }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let array = IntArrayFormulas::from_reader(reader)?; + Ok(Self::new(array)) + } +} + +impl Formula for IntArrayMin { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let array = self.array.evaluate(context)?; + Ok(array.iter().min().copied().unwrap_or_default()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_INTARRAY_MIN); + self.array.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.array.as_ref(), OperatorPriority::Unary, |array| { + format!("min({})", array) + }) + } +} + +struct IntArrayMax { + array: IntArrayFormula, +} + +impl IntArrayMax { + pub fn new(array: IntArrayFormula) -> IntFormula { + Box::new(Self { array }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let array = IntArrayFormulas::from_reader(reader)?; + Ok(Self::new(array)) + } +} + +impl Formula for IntArrayMax { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let array = self.array.evaluate(context)?; + Ok(array.iter().max().copied().unwrap_or_default()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_INTARRAY_MAX); + self.array.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.array.as_ref(), OperatorPriority::Unary, |array| { + format!("max({})", array) + }) + } +} + +struct IntArraySum { + array: IntArrayFormula, +} + +impl IntArraySum { + pub fn new(array: IntArrayFormula) -> IntFormula { + Box::new(Self { array }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let array = IntArrayFormulas::from_reader(reader)?; + Ok(Self::new(array)) + } +} + +impl Formula for IntArraySum { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let array = self.array.evaluate(context)?; + Ok(array.iter().sum()) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_INTARRAY_SUM); + self.array.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.array.as_ref(), OperatorPriority::Unary, |array| { + format!("sum({})", array) + }) + } +} + +struct IntArrayAvg { + array: IntArrayFormula, +} + +impl IntArrayAvg { + pub fn new(array: IntArrayFormula) -> IntFormula { + Box::new(Self { array }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let array = IntArrayFormulas::from_reader(reader)?; + Ok(Self::new(array)) + } +} + +impl Formula for IntArrayAvg { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let array = self.array.evaluate(context)?; + let sum: i64 = array.iter().sum(); + let count = array.len() as i64; + if count == 0 { + return Ok(0); + } + let avg = sum / count; + Ok(avg) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_INTARRAY_AVG); + self.array.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.array.as_ref(), OperatorPriority::Unary, |array| { + format!("avg({})", array) + }) + } +} + +struct IntArrayMedian { + array: IntArrayFormula, +} + +impl IntArrayMedian { + pub fn new(array: IntArrayFormula) -> IntFormula { + Box::new(Self { array }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let array = IntArrayFormulas::from_reader(reader)?; + Ok(Self::new(array)) + } +} + +impl Formula for IntArrayMedian { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let mut array = self.array.evaluate(context)?; + array.sort(); + let count = array.len(); + if count == 0 { + return Ok(0); + } + let median = if count % 2 == 0 { + let left = array[count / 2 - 1]; + let right = array[count / 2]; + (left + right) / 2 + } else { + array[count / 2] + }; + Ok(median) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + writer.write_byte(INT_INTARRAY_MEDIAN); + self.array.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::unary(self.array.as_ref(), OperatorPriority::Unary, |array| { + format!("median({})", array) + }) + } +} + +struct ArrayCount { + array: Box>>, + predicate: BoolFormula, +} + +impl ArrayCount { + pub fn new(array: Box>>, predicate: BoolFormula) -> IntFormula { + Box::new(Self { array, predicate }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let array = T::ARRAY::formula_from_reader(reader)?; + let predicate = BoolFormulas::from_reader(reader)?; + Ok(Self::new(array, predicate)) + } +} + +impl Formula for ArrayCount { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let array = self.array.evaluate(context)?; + let mut local_context = context.clone(); + Ok(array + .iter() + .filter_map(|item| { + let value = T::cast_to_value(item.clone()); + local_context.locals.push(value); + let result = self + .predicate + .evaluate(&local_context) + .map(|v| if v { Some(()) } else { None }) + .transpose(); + local_context.locals.pop(); + result + }) + .count() as i64) + } + + fn serialize_to(&self, writer: &mut crate::formula_writer::FormulaWriter) { + let op = match T::TYPE { + Type::IntArray => INT_INTARRAY_COUNT, + Type::DecimalArray => INT_DECIMALARRAY_COUNT, + Type::StringArray => INT_STRINGARRAY_COUNT, + Type::DateArray => INT_DATEARRAY_COUNT, + Type::DateTimeArray => INT_DATETIMEARRAY_COUNT, + Type::ObjectArray => INT_OBJECTARRAY_COUNT, + _ => panic!("Invalid array type"), + }; + writer.write_byte(op); + self.array.serialize_to(writer); + self.predicate.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.array.as_ref(), + self.predicate.as_ref(), + OperatorPriority::Function, + |array, predicate| format!("{}.count({})", array, predicate), + ) + } +} + +#[cfg(test)] +mod tests {} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5a18d14 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,185 @@ +use bigdecimal::BigDecimal; +use chrono::NaiveDate; +use formula_writer::FormulaWriter; +pub use object::FormulaObject; +use std::sync::Arc; +use types::Type; +use value::Value; + +mod array_formula; +mod bool_formula; +pub mod compilation; +mod date_formula; +mod datetime_formula; +mod decimal_formula; +mod formula_reader; +mod formula_string; +mod formula_writer; +mod generic; +mod int_formula; +mod object; +mod object_formula; +mod string_formula; +mod string_utils; +mod types; +mod value; + +pub use array_formula::BoolArrayFormulas; +pub use array_formula::DateArrayFormulas; +pub use array_formula::DateTimeArrayFormulas; +pub use array_formula::DecimalArrayFormulas; +pub use array_formula::IntArrayFormulas; +pub use array_formula::ObjectArrayFormulas; +pub use array_formula::StringArrayFormulas; +pub use bool_formula::BoolFormulas; +pub use date_formula::DateFormulas; +pub use datetime_formula::DateTimeFormulas; +pub use decimal_formula::DecimalFormulas; +pub use int_formula::IntFormulas; +pub use object::EmptyObject; +pub use object_formula::ObjectFormulas; +pub use string_formula::StringFormulas; + +#[derive(Clone)] +pub struct FormulaContext { + pub root: Arc, + pub locals: Vec, +} + +impl FormulaContext { + pub fn new(root: Arc) -> Self { + Self { + root, + locals: Vec::new(), + } + } + + pub fn new_with_empty_root() -> Self { + Self { + root: EmptyObject::new(), + locals: Vec::new(), + } + } + + pub fn get_local(&self, index: usize) -> Option { + self.locals.get(index).cloned() + } +} + +#[derive(Debug, PartialEq)] +pub enum ExecutionError { + DivideByZero, + CannotTakeSqrtOfNegativeInt(i64), + CannotTakeSqrtOfNegativeDecimal(BigDecimal), + DecimalTooLargeForInt(BigDecimal), + NotA(Type), + NoSuchLocal(usize), + NoSuchField { + typename: String, + field: String, + }, + NoSuchMethod { + typename: String, + method: String, + }, + EmptyArray, + IndexOutOfBounds(usize), + CannotParseToInt(String), + CannotParseToDecimal(String), + InvalidFormula(String), + InvalidISODate(String), + InvalidISODateTime(String), + InvalidDate { + year: i32, + month: u32, + day: u32, + }, + InvalidDateTime { + year: i32, + month: u32, + day: u32, + hour: u32, + minute: u32, + second: u32, + }, + TimezoneConversionAmbiguous { + year: i32, + month: u32, + day: u32, + hour: u32, + minute: u32, + second: u32, + from_tz: String, + to_tz: String, + }, + TimezoneConversionImpossible { + year: i32, + month: u32, + day: u32, + hour: u32, + minute: u32, + second: u32, + from_tz: String, + to_tz: String, + }, +} + +pub type ExecutionResult = Result; + +pub trait Formula { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult; + + fn serialize_to(&self, writer: &mut FormulaWriter); + + fn serialize(&self) -> Vec { + let mut writer = FormulaWriter::new(); + self.serialize_to(&mut writer); + writer.into_bytes() + } + + fn to_string(&self) -> String { + self.to_formula_string().value + } + + fn to_formula_string(&self) -> formula_string::FormulaString; + + fn is_root(&self) -> bool { + false + } +} + +pub type BoolFormula = Box>; +pub type IntFormula = Box>; +pub type DecimalFormula = Box>; +pub type StringFormula = Box>; +pub type DateFormula = Box>; +pub type DateTimeFormula = Box>; +pub type ObjectFormula = Box>>; +pub type BoolArrayFormula = Box>>; +pub type IntArrayFormula = Box>>; +pub type DecimalArrayFormula = Box>>; +pub type StringArrayFormula = Box>>; +pub type DateArrayFormula = Box>>; +pub type DateTimeArrayFormula = Box>>; +pub type ObjectArrayFormula = Box>>>; +pub type ValueFormula = Box>; + +#[derive(Debug)] +pub enum DeserializationError { + UnknownOperator(Type, u8), + UnknownComparator(u8), + UnexpectedEndOfData, + UnknownType(u8), + InvalidCharacter, + InvalidUtf8, + InvalidRoundingMode(u8), + InvalidDate(i32), + InvalidTime(u32), + InvalidRegex(String), + InvalidPrecision(u32), + InvalidTimezoneKind(u8), + InvalidTimezoneOffset(i32), + InvalidTimezoneName(String), +} + +pub type DeserializedResult = Result; diff --git a/src/object.rs b/src/object.rs new file mode 100644 index 0000000..86c2467 --- /dev/null +++ b/src/object.rs @@ -0,0 +1,122 @@ +use std::sync::Arc; + +use crate::{value::Value, ExecutionError}; + +pub trait FormulaObject: Send + Sync { + fn get(&self, field: &str) -> Result; + fn call(&self, method: &str, args: Vec) -> Result; +} + +pub struct EmptyObject; + +impl EmptyObject { + pub fn new() -> Arc { + Arc::new(Self {}) + } +} + +impl FormulaObject for EmptyObject { + fn get(&self, field: &str) -> Result { + Err(ExecutionError::NoSuchField { + typename: "{}".into(), + field: field.to_string(), + }) + } + + fn call(&self, method: &str, _args: Vec) -> Result { + Err(ExecutionError::NoSuchMethod { + typename: "{}".into(), + method: method.to_string(), + }) + } +} + +#[cfg(test)] +pub struct TestObject { + id: i64, +} + +#[cfg(test)] +impl TestObject { + pub fn new(id: i64) -> Arc { + Arc::new(Self { id }) + } +} + +#[cfg(test)] +impl FormulaObject for TestObject { + fn get(&self, field: &str) -> Result { + match field { + "id" => Ok(Value::Int(self.id)), + "bool_true" => Ok(Value::Bool(true)), + "bool_false" => Ok(Value::Bool(false)), + "integer" => Ok(Value::Int(42)), + "decimal" => Ok(Value::Decimal("42.0".parse().unwrap())), + "string" => Ok(Value::String("hello".into())), + "date" => Ok(Value::Date("2020-01-02".parse().unwrap())), + "datetime" => Ok(Value::DateTime("2020-01-02T03:04:05Z".parse().unwrap())), + "empty_bool_array" => Ok(Value::BoolArray(vec![])), + "bool_array" => Ok(Value::BoolArray(vec![true, false])), + "empty_int_array" => Ok(Value::IntArray(vec![])), + "int_array" => Ok(Value::IntArray(vec![1, 2])), + "empty_decimal_array" => Ok(Value::DecimalArray(vec![])), + "decimal_array" => Ok(Value::DecimalArray(vec!["1.0".parse().unwrap()])), + "empty_string_array" => Ok(Value::StringArray(vec![])), + "string_array" => Ok(Value::StringArray(vec!["hello".into(), "world".into()])), + "empty_date_array" => Ok(Value::DateArray(vec![])), + "date_array" => Ok(Value::DateArray(vec![ + "2020-01-02".parse().unwrap(), + "2020-01-03".parse().unwrap(), + ])), + "empty_datetime_array" => Ok(Value::DateTimeArray(vec![])), + "datetime_array" => Ok(Value::DateTimeArray(vec![ + "2020-01-02T03:04:05Z".parse().unwrap(), + "2020-01-03T03:04:05Z".parse().unwrap(), + ])), + "object_empty_array" => Ok(Value::ObjectArray(vec![])), + "object_array" => Ok(Value::ObjectArray(vec![ + Arc::new(TestObject { id: 1 }), + Arc::new(TestObject { id: 2 }), + ])), + _ => Err(ExecutionError::NoSuchField { + typename: "TestObject".into(), + field: field.to_string(), + }), + } + } + + fn call(&self, method: &str, args: Vec) -> Result { + match method { + "really" => Ok(Value::Bool(true)), + "baz" => Ok(Value::Int(args.len() as i64)), + _ => Err(ExecutionError::NoSuchMethod { + typename: "TestObject".into(), + method: method.to_string(), + }), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_empty_object() { + let empty = EmptyObject::new(); + assert_eq!( + empty.get("foo").err(), + Some(ExecutionError::NoSuchField { + typename: "{}".into(), + field: "foo".into() + }) + ); + assert_eq!( + empty.call("foo", vec![]).err(), + Some(ExecutionError::NoSuchMethod { + typename: "{}".into(), + method: "foo".into() + }) + ); + } +} diff --git a/src/object_formula.rs b/src/object_formula.rs new file mode 100644 index 0000000..9c19e93 --- /dev/null +++ b/src/object_formula.rs @@ -0,0 +1,156 @@ +use std::sync::Arc; + +use crate::{ + formula_reader::FormulaReader, + formula_string::{FormulaString, OperatorPriority}, + generic::*, + object::{EmptyObject, FormulaObject}, + types::{ObjectType, Type}, + value::EnumValueFormula, + BoolFormula, DeserializationError, ExecutionResult, Formula, IntFormula, ObjectArrayFormula, + ObjectFormula, +}; + +const OBJECT_OP_ROOT: u8 = 0x10; +const OBJECT_EMPTY: u8 = 0x11; + +pub struct ObjectFormulas; + +impl ObjectFormulas { + pub fn from_bytes( + bytes: &[u8], + ) -> Result>>, DeserializationError> { + let mut reader = FormulaReader::new(bytes); + Self::from_reader(&mut reader) + } + + pub fn from_reader( + reader: &mut FormulaReader, + ) -> Result>>, DeserializationError> { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT => ArrayElement::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT => { + ArrayElementOrDefault::::deserialize(reader) + } + GENERIC_OP_ARRAY_FIRST => ArrayFirst::::deserialize(reader), + GENERIC_OP_ARRAY_LAST => ArrayLast::::deserialize(reader), + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + OBJECT_OP_ROOT => Ok(ObjectRoot::new()), + OBJECT_EMPTY => Ok(ObjectEmpty::new()), + other => Err(DeserializationError::UnknownOperator(Type::Object, other)), + } + } + + pub fn define_locals(locals: Vec, formula: ObjectFormula) -> ObjectFormula { + DefineLocals::::new(locals, formula) + } + + pub fn local(index: u32) -> ObjectFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, name: String) -> ObjectFormula { + ObjectField::::new(object, name) + } + + pub fn object_method_call( + object: ObjectFormula, + name: String, + arguments: Vec, + ) -> ObjectFormula { + ObjectMethodCall::::new(object, name, arguments) + } + + pub fn array_element(array: ObjectArrayFormula, index: IntFormula) -> ObjectFormula { + ArrayElement::::new(array, index) + } + + pub fn array_element_or_default( + array: ObjectArrayFormula, + index: IntFormula, + default: ObjectFormula, + ) -> ObjectFormula { + ArrayElementOrDefault::::new(array, index, default) + } + + pub fn array_first(array: ObjectArrayFormula) -> ObjectFormula { + ArrayFirst::::new(array) + } + + pub fn array_last(array: ObjectArrayFormula) -> ObjectFormula { + ArrayLast::::new(array) + } + + pub fn ternary( + condition: BoolFormula, + if_true: ObjectFormula, + if_false: ObjectFormula, + ) -> ObjectFormula { + Ternary::::new(condition, if_true, if_false) + } + + pub fn root() -> ObjectFormula { + ObjectRoot::new() + } + + pub fn empty() -> ObjectFormula { + ObjectEmpty::new() + } +} + +struct ObjectRoot {} + +impl ObjectRoot { + pub fn new() -> ObjectFormula { + Box::new(Self {}) + } +} + +impl Formula> for ObjectRoot { + fn evaluate(&self, context: &crate::FormulaContext) -> ExecutionResult> { + Ok(context.root.clone()) + } + + fn serialize_to(&self, writer: &mut crate::FormulaWriter) { + writer.write_byte(OBJECT_OP_ROOT); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new("root".to_string(), OperatorPriority::Literal) + } + + fn is_root(&self) -> bool { + true + } +} + +struct ObjectEmpty {} + +impl ObjectEmpty { + pub fn new() -> ObjectFormula { + Box::new(Self {}) + } +} + +impl Formula> for ObjectEmpty { + fn evaluate( + &self, + _context: &crate::FormulaContext, + ) -> ExecutionResult> { + Ok(EmptyObject::new()) + } + + fn serialize_to(&self, writer: &mut crate::FormulaWriter) { + writer.write_byte(OBJECT_EMPTY); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new("empty".to_string(), OperatorPriority::Literal) + } +} diff --git a/src/string_formula.rs b/src/string_formula.rs new file mode 100644 index 0000000..9e8aecd --- /dev/null +++ b/src/string_formula.rs @@ -0,0 +1,817 @@ +use crate::array_formula::StringArrayFormulas; +use crate::formula_reader::FormulaReader; +use crate::formula_string::{FormulaString, OperatorPriority}; +use crate::formula_writer::FormulaWriter; +use crate::string_utils::escape_string; +use crate::types::{StringType, Type}; +use crate::value::EnumValueFormula; +use crate::{generic::*, ObjectFormula}; +use crate::{ + BoolFormula, BoolFormulas, DecimalFormula, DecimalFormulas, DeserializationError, + DeserializedResult, ExecutionResult, Formula, FormulaContext, IntFormula, IntFormulas, + StringArrayFormula, StringFormula, +}; +use regex::Regex; + +const STRING_OP_EMPTY: u8 = 0x10; +const STRING_OP_VALUE: u8 = 0x11; +const STRING_OP_CONCAT: u8 = 0x12; +const STRING_OP_SUBSTRING: u8 = 0x13; +const STRING_OP_LEFTPAD: u8 = 0x14; +const STRING_OP_RIGHTPAD: u8 = 0x15; +const STRING_OP_TRIM: u8 = 0x16; +const STRING_OP_TOLOWER: u8 = 0x17; +const STRING_OP_TOUPPER: u8 = 0x18; +const STRING_OP_TERNARY: u8 = 0x19; +const STRING_OP_FROM_BOOL: u8 = 0x1a; +const STRING_OP_FROM_INT: u8 = 0x1b; +const STRING_OP_FROM_DECIMAL: u8 = 0x1c; +const STRING_EXTRACT_REGEX: u8 = 0x1d; +const STRING_JOIN: u8 = 0x1e; + +pub struct StringFormulas; + +impl StringFormulas { + pub fn from_bytes(bytes: &[u8]) -> Result>, DeserializationError> { + let mut reader = FormulaReader::new(bytes); + Self::from_reader(&mut reader) + } + + pub fn from_reader( + reader: &mut FormulaReader, + ) -> Result>, DeserializationError> { + let operator = reader.read_byte()?; + match operator { + GENERIC_OP_INVALID => InvalidFormula::::deserialize(reader), + GENERIC_OP_DEFINE_LOCALS => DefineLocals::::deserialize(reader), + GENERIC_OP_LOCAL => LocalVariable::::deserialize(reader), + GENERIC_OP_OBJECT_FIELD => ObjectField::::deserialize(reader), + GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT => ArrayElement::::deserialize(reader), + GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT => { + ArrayElementOrDefault::::deserialize(reader) + } + GENERIC_OP_ARRAY_FIRST => ArrayFirst::::deserialize(reader), + GENERIC_OP_ARRAY_LAST => ArrayLast::::deserialize(reader), + GENERIC_OP_TERNARY => Ternary::::deserialize(reader), + STRING_OP_EMPTY => Ok(StringEmpty::new()), + STRING_OP_VALUE => StringValue::deserialize(reader), + STRING_OP_CONCAT => StringConcat::deserialize(reader), + STRING_OP_SUBSTRING => StringSubstring::deserialize(reader), + STRING_OP_LEFTPAD => StringLeftPad::deserialize(reader), + STRING_OP_RIGHTPAD => StringRightPad::deserialize(reader), + STRING_OP_TRIM => StringTrim::deserialize(reader), + STRING_OP_TOLOWER => StringToLower::deserialize(reader), + STRING_OP_TOUPPER => StringToUpper::deserialize(reader), + STRING_OP_TERNARY => StringTernary::deserialize(reader), + STRING_OP_FROM_BOOL => StringFromBool::deserialize(reader), + STRING_OP_FROM_INT => StringFromInt::deserialize(reader), + STRING_OP_FROM_DECIMAL => StringFromDecimal::deserialize(reader), + STRING_EXTRACT_REGEX => StringExtractRegex::deserialize(reader), + STRING_JOIN => StringJoin::deserialize(reader), + other => Err(DeserializationError::UnknownOperator(Type::String, other)), + } + } + + pub fn define_locals(locals: Vec, body: StringFormula) -> StringFormula { + DefineLocals::::new(locals, body) + } + + pub fn local(index: u32) -> StringFormula { + LocalVariable::::new(index) + } + + pub fn object_field(object: ObjectFormula, field: String) -> StringFormula { + ObjectField::::new(object, field) + } + + pub fn object_method_call( + object: ObjectFormula, + method: String, + arguments: Vec, + ) -> StringFormula { + ObjectMethodCall::::new(object, method, arguments) + } + + pub fn array_element(array: StringArrayFormula, index: IntFormula) -> StringFormula { + ArrayElement::::new(array, index) + } + + pub fn array_element_or_default( + array: StringArrayFormula, + index: IntFormula, + default: StringFormula, + ) -> StringFormula { + ArrayElementOrDefault::::new(array, index, default) + } + + pub fn array_first(array: StringArrayFormula) -> StringFormula { + ArrayFirst::::new(array) + } + + pub fn array_last(array: StringArrayFormula) -> StringFormula { + ArrayLast::::new(array) + } + + pub fn ternary( + condition: BoolFormula, + left: StringFormula, + right: StringFormula, + ) -> StringFormula { + Ternary::::new(condition, left, right) + } + + pub fn empty() -> StringFormula { + StringEmpty::new() + } + + pub fn value(value: String) -> StringFormula { + StringValue::new(value) + } + + pub fn concat(left: StringFormula, right: StringFormula) -> StringFormula { + StringConcat::new(left, right) + } + + pub fn substring( + string: StringFormula, + start: IntFormula, + length: IntFormula, + ) -> StringFormula { + StringSubstring::new(string, start, length) + } + + pub fn left_pad(string: StringFormula, length: IntFormula, padding: char) -> StringFormula { + StringLeftPad::new(string, length, padding) + } + + pub fn right_pad(string: StringFormula, length: IntFormula, padding: char) -> StringFormula { + StringRightPad::new(string, length, padding) + } + + pub fn trim(string: StringFormula) -> StringFormula { + StringTrim::new(string) + } + + pub fn to_lower(string: StringFormula) -> StringFormula { + StringToLower::new(string) + } + + pub fn to_upper(string: StringFormula) -> StringFormula { + StringToUpper::new(string) + } + + pub fn from_bool(operand: BoolFormula) -> StringFormula { + StringFromBool::new(operand) + } + + pub fn from_int(operand: IntFormula) -> StringFormula { + StringFromInt::new(operand) + } + + pub fn from_decimal(operand: DecimalFormula) -> StringFormula { + StringFromDecimal::new(operand) + } + + pub fn extract_regex(string: StringFormula, regex: Regex, index: u32) -> StringFormula { + StringExtractRegex::new(string, regex, index) + } + + pub fn join(values: StringArrayFormula, separator: String) -> StringFormula { + StringJoin::new(values, separator) + } +} + +struct StringEmpty {} + +impl StringEmpty { + pub fn new() -> StringFormula { + Box::new(Self {}) + } +} + +impl Formula for StringEmpty { + fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult { + Ok("".into()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_EMPTY); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new("\"\"".to_string(), OperatorPriority::Literal) + } +} + +struct StringValue { + value: String, +} + +impl StringValue { + pub fn new(value: String) -> StringFormula { + Box::new(Self { value }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let value = reader.read_string()?; + Ok(Self::new(value)) + } +} + +impl Formula for StringValue { + fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult { + Ok(self.value.clone()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_VALUE); + writer.write_string(&self.value); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::new(escape_string(&self.value), OperatorPriority::Literal) + } +} + +struct StringConcat { + left: StringFormula, + right: StringFormula, +} + +impl StringConcat { + pub fn new(left: StringFormula, right: StringFormula) -> StringFormula { + Box::new(Self { left, right }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let left = StringFormulas::from_reader(reader)?; + let right = StringFormulas::from_reader(reader)?; + Ok(Self::new(left, right)) + } +} + +impl Formula for StringConcat { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let left = self.left.evaluate(context)?; + let right = self.right.evaluate(context)?; + Ok(left + &right) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_CONCAT); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + FormulaString::binary( + self.left.as_ref(), + self.right.as_ref(), + OperatorPriority::Sum, + |left, right| format!("{} + {}", left, right), + ) + } +} + +struct StringSubstring { + string: StringFormula, + start: IntFormula, + length: IntFormula, +} + +impl StringSubstring { + pub fn new(string: StringFormula, start: IntFormula, length: IntFormula) -> StringFormula { + Box::new(Self { + string, + start, + length, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let string = StringFormulas::from_reader(reader)?; + let start = IntFormulas::from_reader(reader)?; + let length = IntFormulas::from_reader(reader)?; + Ok(Self::new(string, start, length)) + } +} + +impl Formula for StringSubstring { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let string = self.string.evaluate(context)?; + let start = self.start.evaluate(context)?; + let length = self.length.evaluate(context)?; + let start = start as usize; + let length = length as usize; + Ok(string[start..start + length].to_string()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_SUBSTRING); + self.string.serialize_to(writer); + self.start.serialize_to(writer); + self.length.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let value = self + .string + .to_formula_string() + .wrap(OperatorPriority::Member); + let start = self + .start + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + let length = self + .length + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("{}.substring({}, {})", value, start, length), + OperatorPriority::Member, + ) + } +} + +struct StringLeftPad { + string: StringFormula, + length: IntFormula, + padding: char, +} + +impl StringLeftPad { + pub fn new(string: StringFormula, length: IntFormula, padding: char) -> StringFormula { + Box::new(Self { + string, + length, + padding, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let string = StringFormulas::from_reader(reader)?; + let length = IntFormulas::from_reader(reader)?; + let padding = reader.read_char()?; + Ok(Self::new(string, length, padding)) + } +} + +impl Formula for StringLeftPad { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let string = self.string.evaluate(context)?; + let min_length = self.length.evaluate(context)? as usize; + if string.len() >= min_length { + return Ok(string); + } + let length = min_length - string.len(); + let prefix = self.padding.to_string().repeat(length); + Ok(prefix + &string) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_LEFTPAD); + self.string.serialize_to(writer); + self.length.serialize_to(writer); + writer.write_char(self.padding); + } + + fn to_formula_string(&self) -> FormulaString { + let value = self + .string + .to_formula_string() + .wrap(OperatorPriority::Member); + let length = self + .length + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("{}.pad_left({}, '{}')", value, length, self.padding), + OperatorPriority::Member, + ) + } +} + +struct StringRightPad { + string: StringFormula, + length: IntFormula, + padding: char, +} + +impl StringRightPad { + pub fn new(string: StringFormula, length: IntFormula, padding: char) -> StringFormula { + Box::new(Self { + string, + length, + padding, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let string = StringFormulas::from_reader(reader)?; + let length = IntFormulas::from_reader(reader)?; + let padding = reader.read_char()?; + Ok(Self::new(string, length, padding)) + } +} + +impl Formula for StringRightPad { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let string = self.string.evaluate(context)?; + let min_length = self.length.evaluate(context)? as usize; + if string.len() >= min_length { + return Ok(string); + } + let length = min_length - string.len(); + let suffix = self.padding.to_string().repeat(length); + Ok(string + &suffix) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_RIGHTPAD); + self.string.serialize_to(writer); + self.length.serialize_to(writer); + writer.write_char(self.padding); + } + + fn to_formula_string(&self) -> FormulaString { + let value = self + .string + .to_formula_string() + .wrap(OperatorPriority::Member); + let length = self + .length + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new( + format!("{}.pad_right({}, '{}')", value, length, self.padding), + OperatorPriority::Member, + ) + } +} + +struct StringTrim { + string: StringFormula, +} + +impl StringTrim { + pub fn new(string: StringFormula) -> StringFormula { + Box::new(Self { string }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let string = StringFormulas::from_reader(reader)?; + Ok(Self::new(string)) + } +} + +impl Formula for StringTrim { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let string = self.string.evaluate(context)?; + Ok(string.trim().to_string()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_TRIM); + self.string.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let value = self + .string + .to_formula_string() + .wrap(OperatorPriority::Member); + FormulaString::new(format!("{}.trim()", value), OperatorPriority::Member) + } +} + +struct StringToLower { + string: StringFormula, +} + +impl StringToLower { + pub fn new(string: StringFormula) -> StringFormula { + Box::new(Self { string }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let string = StringFormulas::from_reader(reader)?; + Ok(Self::new(string)) + } +} + +impl Formula for StringToLower { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let string = self.string.evaluate(context)?; + Ok(string.to_lowercase()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_TOLOWER); + self.string.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let value = self + .string + .to_formula_string() + .wrap(OperatorPriority::Member); + FormulaString::new(format!("{}.to_lower()", value), OperatorPriority::Member) + } +} + +struct StringToUpper { + string: StringFormula, +} + +impl StringToUpper { + pub fn new(string: StringFormula) -> StringFormula { + Box::new(Self { string }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let string = StringFormulas::from_reader(reader)?; + Ok(Self::new(string)) + } +} + +impl Formula for StringToUpper { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let string = self.string.evaluate(context)?; + Ok(string.to_uppercase()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_TOUPPER); + self.string.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let value = self + .string + .to_formula_string() + .wrap(OperatorPriority::Member); + FormulaString::new(format!("{}.to_upper()", value), OperatorPriority::Member) + } +} + +struct StringTernary { + condition: BoolFormula, + left: StringFormula, + right: StringFormula, +} + +impl StringTernary { + pub fn new(condition: BoolFormula, left: StringFormula, right: StringFormula) -> StringFormula { + Box::new(Self { + condition, + left, + right, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let condition = BoolFormulas::from_reader(reader)?; + let left = StringFormulas::from_reader(reader)?; + let right = StringFormulas::from_reader(reader)?; + Ok(Self::new(condition, left, right)) + } +} + +impl Formula for StringTernary { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let condition = self.condition.evaluate(context)?; + if condition { + self.left.evaluate(context) + } else { + self.right.evaluate(context) + } + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_TERNARY); + self.condition.serialize_to(writer); + self.left.serialize_to(writer); + self.right.serialize_to(writer); + } + + fn to_formula_string(&self) -> FormulaString { + let condition = self + .condition + .to_formula_string() + .wrap(OperatorPriority::None); + let left = self.left.to_formula_string().wrap(OperatorPriority::None); + let right = self.right.to_formula_string().wrap(OperatorPriority::None); + FormulaString::new( + format!("if {} then {} else {}", condition, left, right), + OperatorPriority::None, + ) + } +} + +struct StringFromBool { + operand: BoolFormula, +} + +impl StringFromBool { + pub fn new(operand: BoolFormula) -> StringFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = BoolFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for StringFromBool { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + Ok(operand.to_string()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_FROM_BOOL); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new(format!("string({})", operand), OperatorPriority::Function) + } +} + +struct StringFromInt { + operand: IntFormula, +} + +impl StringFromInt { + pub fn new(operand: IntFormula) -> StringFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = IntFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for StringFromInt { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + Ok(operand.to_string()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_FROM_INT); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new(format!("string({})", operand), OperatorPriority::Function) + } +} + +struct StringFromDecimal { + operand: DecimalFormula, +} + +impl StringFromDecimal { + pub fn new(operand: DecimalFormula) -> StringFormula { + Box::new(Self { operand }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let operand = DecimalFormulas::from_reader(reader)?; + Ok(Self::new(operand)) + } +} + +impl Formula for StringFromDecimal { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let operand = self.operand.evaluate(context)?; + Ok(operand.to_string()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_OP_FROM_DECIMAL); + self.operand.serialize_to(writer); + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + let operand = self + .operand + .to_formula_string() + .wrap(OperatorPriority::Parentheses); + FormulaString::new(format!("string({})", operand), OperatorPriority::Function) + } +} + +struct StringExtractRegex { + string: StringFormula, + regex: Regex, + index: u32, +} + +impl StringExtractRegex { + pub fn new(string: StringFormula, regex: Regex, index: u32) -> StringFormula { + Box::new(Self { + string, + regex, + index, + }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let string = StringFormulas::from_reader(reader)?; + let regex_string = reader.read_string()?; + let regex = Regex::new(®ex_string) + .map_err(|_| DeserializationError::InvalidRegex(regex_string))?; + let index = reader.read_u32()?; + Ok(Self::new(string, regex, index)) + } +} + +impl Formula for StringExtractRegex { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let string = self.string.evaluate(context)?; + let captures = self.regex.captures(&string); + let capture = captures + .map(|capture| capture.get(self.index as usize)) + .flatten() + .map(|capture| capture.as_str()) + .unwrap_or_default(); + Ok(capture.to_string()) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_EXTRACT_REGEX); + self.string.serialize_to(writer); + writer.write_string(self.regex.as_str()); + writer.write_u32(self.index); + } + + fn to_formula_string(&self) -> FormulaString { + let value = self + .string + .to_formula_string() + .wrap(OperatorPriority::Member); + FormulaString::new( + format!( + "{}.extract_regex('{}', {})", + value, + self.regex.as_str(), + self.index + ), + OperatorPriority::Member, + ) + } +} + +struct StringJoin { + values: StringArrayFormula, + separator: String, +} + +impl StringJoin { + pub fn new(values: StringArrayFormula, separator: String) -> StringFormula { + Box::new(Self { values, separator }) + } + + pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult { + let values = StringArrayFormulas::from_reader(reader)?; + let separator = reader.read_string()?; + Ok(Self::new(values, separator)) + } +} + +impl Formula for StringJoin { + fn evaluate(&self, context: &FormulaContext) -> ExecutionResult { + let values = self.values.evaluate(context)?; + Ok(values.join(&self.separator)) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(STRING_JOIN); + self.values.serialize_to(writer); + writer.write_string(&self.separator); + } + + fn to_formula_string(&self) -> FormulaString { + let values = self + .values + .to_formula_string() + .wrap(OperatorPriority::Member); + FormulaString::new( + format!("{}.join('{}')", values, self.separator), + OperatorPriority::Member, + ) + } +} diff --git a/src/string_utils.rs b/src/string_utils.rs new file mode 100644 index 0000000..2d1a473 --- /dev/null +++ b/src/string_utils.rs @@ -0,0 +1,12 @@ +// TODO: proper formatting of special characters + +pub fn unescape_string(value: &str) -> String { + let mut chars = value.chars(); + chars.next(); + chars.next_back(); + chars.collect() +} + +pub fn escape_string(value: &str) -> String { + format!("\"{}\"", value.replace("\"", "\\\"")) +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..1bdd7b9 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,511 @@ +use std::sync::Arc; + +use bigdecimal::BigDecimal; +use chrono::{NaiveDate, NaiveDateTime}; + +use crate::{ + array_formula::{ + BoolArrayFormulas, DateArrayFormulas, DateTimeArrayFormulas, DecimalArrayFormulas, + IntArrayFormulas, ObjectArrayFormulas, StringArrayFormulas, + }, + formula_reader::FormulaReader, + object::FormulaObject, + value::{EnumValueFormula, Value}, + BoolArrayFormula, BoolFormula, BoolFormulas, DateArrayFormula, DateFormula, DateFormulas, + DateTimeArrayFormula, DateTimeFormula, DateTimeFormulas, DecimalArrayFormula, DecimalFormula, + DecimalFormulas, DeserializationError, DeserializedResult, Formula, IntArrayFormula, + IntFormula, IntFormulas, ObjectArrayFormula, ObjectFormula, ObjectFormulas, StringArrayFormula, + StringFormula, StringFormulas, +}; + +#[derive(Clone, Debug, PartialEq)] +pub enum Type { + Bool, + Int, + Decimal, + String, + Date, + DateTime, + Object, + BoolArray, + IntArray, + DecimalArray, + StringArray, + DateArray, + DateTimeArray, + ObjectArray, +} + +impl Type { + pub fn deserialize(value: u8) -> Result { + match value { + 0 => Ok(Type::Bool), + 1 => Ok(Type::Int), + 2 => Ok(Type::Decimal), + 3 => Ok(Type::String), + 4 => Ok(Type::Date), + 5 => Ok(Type::DateTime), + 6 => Ok(Type::Object), + 7 => Ok(Type::BoolArray), + 8 => Ok(Type::IntArray), + 9 => Ok(Type::DecimalArray), + 10 => Ok(Type::StringArray), + 11 => Ok(Type::DateArray), + 12 => Ok(Type::DateTimeArray), + 13 => Ok(Type::ObjectArray), + other => Err(DeserializationError::UnknownType(other)), + } + } + + pub fn serialize(&self) -> u8 { + match self { + Type::Bool => 0, + Type::Int => 1, + Type::Decimal => 2, + Type::String => 3, + Type::Date => 4, + Type::DateTime => 5, + Type::Object => 6, + Type::BoolArray => 7, + Type::IntArray => 8, + Type::DecimalArray => 9, + Type::StringArray => 10, + Type::DateArray => 11, + Type::DateTimeArray => 12, + Type::ObjectArray => 13, + } + } + + pub fn array(&self) -> Option { + match self { + Type::Bool => Some(Type::BoolArray), + Type::Int => Some(Type::IntArray), + Type::Decimal => Some(Type::DecimalArray), + Type::String => Some(Type::StringArray), + Type::Date => Some(Type::DateArray), + Type::DateTime => Some(Type::DateTimeArray), + Type::Object => Some(Type::ObjectArray), + _ => None, + } + } +} + +pub trait AnyType: 'static { + type T: Clone; + const TYPE: Type; + + fn formula_from_reader( + reader: &mut FormulaReader, + ) -> DeserializedResult>>; + + fn cast_to_value(value: Self::T) -> Value; + + fn cast_from_value(value: Value) -> Option; + + fn cast_from_formula(formula: EnumValueFormula) -> Option>>; + + fn to_enum_formula(formula: Box>) -> EnumValueFormula; +} + +pub trait ScalarType: AnyType { + type ARRAY: AnyType>; +} + +pub struct BoolType; +pub struct BoolArrayType; + +impl AnyType for BoolType { + type T = bool; + const TYPE: Type = Type::Bool; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + BoolFormulas::from_reader(reader) + } + + fn cast_to_value(value: bool) -> Value { + Value::Bool(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_bool() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_bool() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::Bool(formula) + } +} + +impl AnyType for BoolArrayType { + type T = Vec; + const TYPE: Type = Type::BoolArray; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + BoolArrayFormulas::from_reader(reader) + } + + fn cast_to_value(value: Vec) -> Value { + Value::BoolArray(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_bool_array() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_bool_array() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::BoolArray(formula) + } +} + +impl ScalarType for BoolType { + type ARRAY = BoolArrayType; +} + +pub struct IntType; +pub struct IntArrayType; + +impl AnyType for IntType { + type T = i64; + const TYPE: Type = Type::Int; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + IntFormulas::from_reader(reader) + } + + fn cast_to_value(value: i64) -> Value { + Value::Int(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_int() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_int() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::Int(formula) + } +} + +impl AnyType for IntArrayType { + type T = Vec; + const TYPE: Type = Type::IntArray; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + IntArrayFormulas::from_reader(reader) + } + + fn cast_to_value(value: Vec) -> Value { + Value::IntArray(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_int_array() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_int_array() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::IntArray(formula) + } +} + +impl ScalarType for IntType { + type ARRAY = IntArrayType; +} + +pub struct DecimalType; +pub struct DecimalArrayType; + +impl AnyType for DecimalType { + type T = BigDecimal; + const TYPE: Type = Type::Decimal; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + DecimalFormulas::from_reader(reader) + } + + fn cast_to_value(value: BigDecimal) -> Value { + Value::Decimal(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_decimal() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_decimal() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::Decimal(formula) + } +} + +impl AnyType for DecimalArrayType { + type T = Vec; + const TYPE: Type = Type::DecimalArray; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + DecimalArrayFormulas::from_reader(reader) + } + + fn cast_to_value(value: Vec) -> Value { + Value::DecimalArray(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_decimal_array() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_decimal_array() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::DecimalArray(formula) + } +} + +impl ScalarType for DecimalType { + type ARRAY = DecimalArrayType; +} + +pub struct StringType; +pub struct StringArrayType; + +impl AnyType for StringType { + type T = String; + const TYPE: Type = Type::String; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + StringFormulas::from_reader(reader) + } + + fn cast_to_value(value: String) -> Value { + Value::String(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_string() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_string() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::String(formula) + } +} + +impl AnyType for StringArrayType { + type T = Vec; + const TYPE: Type = Type::StringArray; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + StringArrayFormulas::from_reader(reader) + } + + fn cast_to_value(value: Vec) -> Value { + Value::StringArray(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_string_array() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_string_array() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::StringArray(formula) + } +} + +impl ScalarType for StringType { + type ARRAY = StringArrayType; +} + +pub struct DateType; +pub struct DateArrayType; + +impl AnyType for DateType { + type T = NaiveDate; + const TYPE: Type = Type::Date; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + DateFormulas::from_reader(reader) + } + + fn cast_to_value(value: NaiveDate) -> Value { + Value::Date(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_date() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_date() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::Date(formula) + } +} + +impl AnyType for DateArrayType { + type T = Vec; + const TYPE: Type = Type::DateArray; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + DateArrayFormulas::from_reader(reader) + } + + fn cast_to_value(value: Vec) -> Value { + Value::DateArray(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_date_array() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_date_array() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::DateArray(formula) + } +} + +impl ScalarType for DateType { + type ARRAY = DateArrayType; +} + +pub struct DateTimeType; +pub struct DateTimeArrayType; + +impl AnyType for DateTimeType { + type T = NaiveDateTime; + const TYPE: Type = Type::DateTime; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + DateTimeFormulas::from_reader(reader) + } + + fn cast_to_value(value: NaiveDateTime) -> Value { + Value::DateTime(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_datetime() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_datetime() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::DateTime(formula) + } +} + +impl AnyType for DateTimeArrayType { + type T = Vec; + const TYPE: Type = Type::DateTimeArray; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + DateTimeArrayFormulas::from_reader(reader) + } + + fn cast_to_value(value: Vec) -> Value { + Value::DateTimeArray(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_datetime_array() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_datetime_array() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::DateTimeArray(formula) + } +} + +impl ScalarType for DateTimeType { + type ARRAY = DateTimeArrayType; +} + +pub struct ObjectType; +pub struct ObjectArrayType; + +impl AnyType for ObjectType { + type T = Arc; + const TYPE: Type = Type::Object; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + ObjectFormulas::from_reader(reader) + } + + fn cast_to_value(value: Arc) -> Value { + Value::Object(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_object() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_object() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::Object(formula) + } +} + +impl AnyType for ObjectArrayType { + type T = Vec>; + const TYPE: Type = Type::ObjectArray; + + fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult { + ObjectArrayFormulas::from_reader(reader) + } + + fn cast_to_value(value: Vec>) -> Value { + Value::ObjectArray(value) + } + + fn cast_from_value(value: Value) -> Option { + value.to_object_array() + } + + fn cast_from_formula(formula: EnumValueFormula) -> Option>> { + formula.to_object_array() + } + + fn to_enum_formula(formula: Box>) -> EnumValueFormula { + EnumValueFormula::ObjectArray(formula) + } +} + +impl ScalarType for ObjectType { + type ARRAY = ObjectArrayType; +} diff --git a/src/value.rs b/src/value.rs new file mode 100644 index 0000000..85bde68 --- /dev/null +++ b/src/value.rs @@ -0,0 +1,792 @@ +use std::sync::Arc; + +use bigdecimal::{BigDecimal, FromPrimitive}; +use chrono::{NaiveDate, NaiveDateTime}; + +use crate::{ + formula_reader::FormulaReader, formula_writer::FormulaWriter, generic::LocalDefinition, + object::FormulaObject, types::*, BoolArrayFormula, BoolArrayFormulas, BoolFormula, + BoolFormulas, DateArrayFormula, DateArrayFormulas, DateFormula, DateFormulas, + DateTimeArrayFormula, DateTimeArrayFormulas, DateTimeFormula, DateTimeFormulas, + DecimalArrayFormula, DecimalArrayFormulas, DecimalFormula, DecimalFormulas, + DeserializationError, DeserializedResult, ExecutionError, Formula, FormulaContext, + IntArrayFormula, IntArrayFormulas, IntFormula, IntFormulas, ObjectArrayFormula, + ObjectArrayFormulas, ObjectFormula, ObjectFormulas, StringArrayFormula, StringArrayFormulas, + StringFormula, StringFormulas, +}; + +#[derive(Clone)] +pub enum Value { + Bool(bool), + Int(i64), + Decimal(BigDecimal), + String(String), + Date(NaiveDate), + DateTime(NaiveDateTime), + Object(Arc), + BoolArray(Vec), + IntArray(Vec), + DecimalArray(Vec), + StringArray(Vec), + DateArray(Vec), + DateTimeArray(Vec), + ObjectArray(Vec>), +} + +#[derive(Debug, PartialEq)] +pub enum EqValue { + Bool(bool), + Int(i64), + Decimal(BigDecimal), + String(String), + Date(NaiveDate), + DateTime(NaiveDateTime), + BoolArray(Vec), + IntArray(Vec), + DecimalArray(Vec), + StringArray(Vec), + DateArray(Vec), + DateTimeArray(Vec), +} + +impl Value { + pub fn get_type(&self) -> Type { + match self { + Value::Bool(_) => Type::Bool, + Value::Int(_) => Type::Int, + Value::Decimal(_) => Type::Decimal, + Value::String(_) => Type::String, + Value::Date(_) => Type::Date, + Value::DateTime(_) => Type::DateTime, + Value::Object(_) => Type::Object, + Value::BoolArray(_) => Type::BoolArray, + Value::IntArray(_) => Type::IntArray, + Value::DecimalArray(_) => Type::DecimalArray, + Value::StringArray(_) => Type::StringArray, + Value::DateArray(_) => Type::DateArray, + Value::DateTimeArray(_) => Type::DateTimeArray, + Value::ObjectArray(_) => Type::ObjectArray, + } + } + + pub fn to_bool(self) -> Option { + match self { + Value::Bool(value) => Some(value), + _ => None, + } + } + + pub fn to_int(self) -> Option { + match self { + Value::Int(value) => Some(value), + _ => None, + } + } + + pub fn to_decimal(self) -> Option { + match self { + Value::Decimal(value) => Some(value), + _ => None, + } + } + + pub fn to_string(self) -> Option { + match self { + Value::String(value) => Some(value), + _ => None, + } + } + + pub fn to_date(self) -> Option { + match self { + Value::Date(value) => Some(value), + _ => None, + } + } + + pub fn to_datetime(self) -> Option { + match self { + Value::DateTime(value) => Some(value), + _ => None, + } + } + + pub fn to_object(self) -> Option> { + match self { + Value::Object(value) => Some(value), + _ => None, + } + } + + pub fn to_bool_array(self) -> Option> { + match self { + Value::BoolArray(value) => Some(value), + _ => None, + } + } + + pub fn to_int_array(self) -> Option> { + match self { + Value::IntArray(value) => Some(value), + _ => None, + } + } + + pub fn to_decimal_array(self) -> Option> { + match self { + Value::DecimalArray(value) => Some(value), + _ => None, + } + } + + pub fn to_string_array(self) -> Option> { + match self { + Value::StringArray(value) => Some(value), + _ => None, + } + } + + pub fn to_date_array(self) -> Option> { + match self { + Value::DateArray(value) => Some(value), + _ => None, + } + } + + pub fn to_datetime_array(self) -> Option> { + match self { + Value::DateTimeArray(value) => Some(value), + _ => None, + } + } + + pub fn to_object_array(self) -> Option>> { + match self { + Value::ObjectArray(value) => Some(value), + _ => None, + } + } + + pub fn comparable(self) -> Option { + match self { + Value::Bool(value) => Some(EqValue::Bool(value)), + Value::Int(value) => Some(EqValue::Int(value)), + Value::Decimal(value) => Some(EqValue::Decimal(value)), + Value::String(value) => Some(EqValue::String(value)), + Value::Date(value) => Some(EqValue::Date(value)), + Value::DateTime(value) => Some(EqValue::DateTime(value)), + Value::BoolArray(value) => Some(EqValue::BoolArray(value)), + Value::IntArray(value) => Some(EqValue::IntArray(value)), + Value::DecimalArray(value) => Some(EqValue::DecimalArray(value)), + Value::StringArray(value) => Some(EqValue::StringArray(value)), + Value::DateArray(value) => Some(EqValue::DateArray(value)), + Value::DateTimeArray(value) => Some(EqValue::DateTimeArray(value)), + Value::ObjectArray(_) => None, + Value::Object(_) => None, + } + } +} + +pub fn deserialize_arguments_from_reader( + reader: &mut FormulaReader, +) -> Result, DeserializationError> { + let argument_count = reader.read_u32()?; + let mut arguments = Vec::with_capacity(argument_count as usize); + for _ in 0..argument_count { + arguments.push(deserialize_value_formula_from_reader(reader)?); + } + Ok(arguments) +} + +pub fn deserialize_value_formula_from_reader( + reader: &mut FormulaReader, +) -> DeserializedResult { + let type_byte = reader.read_byte()?; + let type_ = Type::deserialize(type_byte)?; + match type_ { + Type::Bool => Ok(EnumValueFormula::Bool(BoolFormulas::from_reader(reader)?)), + Type::Int => Ok(EnumValueFormula::Int(IntFormulas::from_reader(reader)?)), + Type::Decimal => Ok(EnumValueFormula::Decimal(DecimalFormulas::from_reader( + reader, + )?)), + Type::String => Ok(EnumValueFormula::String(StringFormulas::from_reader( + reader, + )?)), + Type::Date => Ok(EnumValueFormula::Date(DateFormulas::from_reader(reader)?)), + Type::DateTime => Ok(EnumValueFormula::DateTime(DateTimeFormulas::from_reader( + reader, + )?)), + Type::Object => Ok(EnumValueFormula::Object(ObjectFormulas::from_reader( + reader, + )?)), + Type::BoolArray => Ok(EnumValueFormula::BoolArray(BoolArrayFormulas::from_reader( + reader, + )?)), + Type::IntArray => Ok(EnumValueFormula::IntArray(IntArrayFormulas::from_reader( + reader, + )?)), + Type::DecimalArray => Ok(EnumValueFormula::DecimalArray( + DecimalArrayFormulas::from_reader(reader)?, + )), + Type::StringArray => Ok(EnumValueFormula::StringArray( + StringArrayFormulas::from_reader(reader)?, + )), + Type::DateArray => Ok(EnumValueFormula::DateArray(DateArrayFormulas::from_reader( + reader, + )?)), + Type::DateTimeArray => Ok(EnumValueFormula::DateTimeArray( + DateTimeArrayFormulas::from_reader(reader)?, + )), + Type::ObjectArray => Ok(EnumValueFormula::ObjectArray( + ObjectArrayFormulas::from_reader(reader)?, + )), + } +} + +pub struct BoxValueFormula { + value: Box>, +} + +impl Formula for BoxValueFormula { + fn evaluate(&self, context: &FormulaContext) -> Result { + let value = self.value.evaluate(context)?; + Ok(T::cast_to_value(value)) + } + + fn serialize_to(&self, writer: &mut FormulaWriter) { + writer.write_byte(T::TYPE.serialize()); + self.value.serialize_to(writer); + } + + fn to_formula_string(&self) -> crate::formula_string::FormulaString { + self.value.to_formula_string() + } +} + +pub enum EnumValueFormula { + Bool(BoolFormula), + Int(IntFormula), + Decimal(DecimalFormula), + String(StringFormula), + Date(DateFormula), + DateTime(DateTimeFormula), + Object(ObjectFormula), + BoolArray(BoolArrayFormula), + IntArray(IntArrayFormula), + DecimalArray(DecimalArrayFormula), + StringArray(StringArrayFormula), + DateArray(DateArrayFormula), + DateTimeArray(DateTimeArrayFormula), + ObjectArray(ObjectArrayFormula), + + IntConstant(i64), + StringConstant(String), + LiteralConstant(String), +} + +impl EnumValueFormula { + pub fn type_(&self) -> Type { + match self { + EnumValueFormula::Bool(_) => Type::Bool, + EnumValueFormula::Int(_) => Type::Int, + EnumValueFormula::Decimal(_) => Type::Decimal, + EnumValueFormula::String(_) => Type::String, + EnumValueFormula::Date(_) => Type::Date, + EnumValueFormula::DateTime(_) => Type::DateTime, + EnumValueFormula::Object(_) => Type::Object, + EnumValueFormula::BoolArray(_) => Type::BoolArray, + EnumValueFormula::IntArray(_) => Type::IntArray, + EnumValueFormula::DecimalArray(_) => Type::DecimalArray, + EnumValueFormula::StringArray(_) => Type::StringArray, + EnumValueFormula::DateArray(_) => Type::DateArray, + EnumValueFormula::DateTimeArray(_) => Type::DateTimeArray, + EnumValueFormula::ObjectArray(_) => Type::ObjectArray, + EnumValueFormula::IntConstant(_) => Type::Int, + EnumValueFormula::StringConstant(_) => Type::String, + EnumValueFormula::LiteralConstant(_) => Type::String, + } + } + + pub fn local(type_: Type, index: u32) -> EnumValueFormula { + match type_ { + Type::Bool => EnumValueFormula::Bool(BoolFormulas::local(index)), + Type::Int => EnumValueFormula::Int(IntFormulas::local(index)), + Type::Decimal => EnumValueFormula::Decimal(DecimalFormulas::local(index)), + Type::String => EnumValueFormula::String(StringFormulas::local(index)), + Type::Date => EnumValueFormula::Date(DateFormulas::local(index)), + Type::DateTime => EnumValueFormula::DateTime(DateTimeFormulas::local(index)), + Type::Object => EnumValueFormula::Object(ObjectFormulas::local(index)), + Type::BoolArray => EnumValueFormula::BoolArray(BoolArrayFormulas::local(index)), + Type::IntArray => EnumValueFormula::IntArray(IntArrayFormulas::local(index)), + Type::DecimalArray => { + EnumValueFormula::DecimalArray(DecimalArrayFormulas::local(index)) + } + Type::StringArray => EnumValueFormula::StringArray(StringArrayFormulas::local(index)), + Type::DateArray => EnumValueFormula::DateArray(DateArrayFormulas::local(index)), + Type::DateTimeArray => { + EnumValueFormula::DateTimeArray(DateTimeArrayFormulas::local(index)) + } + Type::ObjectArray => EnumValueFormula::ObjectArray(ObjectArrayFormulas::local(index)), + } + } + + pub fn field(type_: Type, object: ObjectFormula, field: String) -> EnumValueFormula { + match type_ { + Type::Bool => EnumValueFormula::Bool(BoolFormulas::object_field(object, field)), + Type::Int => EnumValueFormula::Int(IntFormulas::object_field(object, field)), + Type::Decimal => { + EnumValueFormula::Decimal(DecimalFormulas::object_field(object, field)) + } + Type::String => EnumValueFormula::String(StringFormulas::object_field(object, field)), + Type::Date => EnumValueFormula::Date(DateFormulas::object_field(object, field)), + Type::DateTime => { + EnumValueFormula::DateTime(DateTimeFormulas::object_field(object, field)) + } + Type::Object => EnumValueFormula::Object(ObjectFormulas::object_field(object, field)), + Type::BoolArray => { + EnumValueFormula::BoolArray(BoolArrayFormulas::object_field(object, field)) + } + Type::IntArray => { + EnumValueFormula::IntArray(IntArrayFormulas::object_field(object, field)) + } + Type::DecimalArray => { + EnumValueFormula::DecimalArray(DecimalArrayFormulas::object_field(object, field)) + } + Type::StringArray => { + EnumValueFormula::StringArray(StringArrayFormulas::object_field(object, field)) + } + Type::DateArray => { + EnumValueFormula::DateArray(DateArrayFormulas::object_field(object, field)) + } + Type::DateTimeArray => { + EnumValueFormula::DateTimeArray(DateTimeArrayFormulas::object_field(object, field)) + } + Type::ObjectArray => { + EnumValueFormula::ObjectArray(ObjectArrayFormulas::object_field(object, field)) + } + } + } + + pub fn conditional( + type_: Type, + condition: BoolFormula, + then: EnumValueFormula, + else_: EnumValueFormula, + ) -> EnumValueFormula { + match type_ { + Type::Bool => EnumValueFormula::Bool(BoolFormulas::ternary( + condition, + then.to_bool().unwrap(), + else_.to_bool().unwrap(), + )), + Type::Int => EnumValueFormula::Int(IntFormulas::ternary( + condition, + then.to_int().unwrap(), + else_.to_int().unwrap(), + )), + Type::Decimal => EnumValueFormula::Decimal(DecimalFormulas::ternary( + condition, + then.to_decimal().unwrap(), + else_.to_decimal().unwrap(), + )), + Type::String => EnumValueFormula::String(StringFormulas::ternary( + condition, + then.to_string().unwrap(), + else_.to_string().unwrap(), + )), + Type::Date => EnumValueFormula::Date(DateFormulas::ternary( + condition, + then.to_date().unwrap(), + else_.to_date().unwrap(), + )), + Type::DateTime => EnumValueFormula::DateTime(DateTimeFormulas::ternary( + condition, + then.to_datetime().unwrap(), + else_.to_datetime().unwrap(), + )), + Type::Object => EnumValueFormula::Object(ObjectFormulas::ternary( + condition, + then.to_object().unwrap(), + else_.to_object().unwrap(), + )), + Type::BoolArray => EnumValueFormula::BoolArray(BoolArrayFormulas::ternary( + condition, + then.to_bool_array().unwrap(), + else_.to_bool_array().unwrap(), + )), + Type::IntArray => EnumValueFormula::IntArray(IntArrayFormulas::ternary( + condition, + then.to_int_array().unwrap(), + else_.to_int_array().unwrap(), + )), + Type::DecimalArray => EnumValueFormula::DecimalArray(DecimalArrayFormulas::ternary( + condition, + then.to_decimal_array().unwrap(), + else_.to_decimal_array().unwrap(), + )), + Type::StringArray => EnumValueFormula::StringArray(StringArrayFormulas::ternary( + condition, + then.to_string_array().unwrap(), + else_.to_string_array().unwrap(), + )), + Type::DateArray => EnumValueFormula::DateArray(DateArrayFormulas::ternary( + condition, + then.to_date_array().unwrap(), + else_.to_date_array().unwrap(), + )), + Type::DateTimeArray => EnumValueFormula::DateTimeArray(DateTimeArrayFormulas::ternary( + condition, + then.to_datetime_array().unwrap(), + else_.to_datetime_array().unwrap(), + )), + Type::ObjectArray => EnumValueFormula::ObjectArray(ObjectArrayFormulas::ternary( + condition, + then.to_object_array().unwrap(), + else_.to_object_array().unwrap(), + )), + } + } + + pub fn array_element(self, index: IntFormula) -> Option { + match self { + EnumValueFormula::BoolArray(value) => Some(EnumValueFormula::Bool( + BoolFormulas::array_element(value, index), + )), + EnumValueFormula::IntArray(value) => Some(EnumValueFormula::Int( + IntFormulas::array_element(value, index), + )), + EnumValueFormula::DecimalArray(value) => Some(EnumValueFormula::Decimal( + DecimalFormulas::array_element(value, index), + )), + EnumValueFormula::StringArray(value) => Some(EnumValueFormula::String( + StringFormulas::array_element(value, index), + )), + EnumValueFormula::DateArray(value) => Some(EnumValueFormula::Date( + DateFormulas::array_element(value, index), + )), + EnumValueFormula::DateTimeArray(value) => Some(EnumValueFormula::DateTime( + DateTimeFormulas::array_element(value, index), + )), + EnumValueFormula::ObjectArray(value) => Some(EnumValueFormula::Object( + ObjectFormulas::array_element(value, index), + )), + _ => panic!("not an array"), + } + } + + pub fn with_locals(self, locals: Vec) -> EnumValueFormula { + match self { + EnumValueFormula::Bool(value) => { + EnumValueFormula::Bool(BoolFormulas::define_locals(locals, value)) + } + EnumValueFormula::Int(value) => { + EnumValueFormula::Int(IntFormulas::define_locals(locals, value)) + } + EnumValueFormula::Decimal(value) => { + EnumValueFormula::Decimal(DecimalFormulas::define_locals(locals, value)) + } + EnumValueFormula::String(value) => { + EnumValueFormula::String(StringFormulas::define_locals(locals, value)) + } + EnumValueFormula::Date(value) => { + EnumValueFormula::Date(DateFormulas::define_locals(locals, value)) + } + EnumValueFormula::DateTime(value) => { + EnumValueFormula::DateTime(DateTimeFormulas::define_locals(locals, value)) + } + EnumValueFormula::Object(value) => { + EnumValueFormula::Object(ObjectFormulas::define_locals(locals, value)) + } + EnumValueFormula::BoolArray(value) => { + EnumValueFormula::BoolArray(BoolArrayFormulas::define_locals(locals, value)) + } + EnumValueFormula::IntArray(value) => { + EnumValueFormula::IntArray(IntArrayFormulas::define_locals(locals, value)) + } + EnumValueFormula::DecimalArray(value) => { + EnumValueFormula::DecimalArray(DecimalArrayFormulas::define_locals(locals, value)) + } + EnumValueFormula::StringArray(value) => { + EnumValueFormula::StringArray(StringArrayFormulas::define_locals(locals, value)) + } + EnumValueFormula::DateArray(value) => { + EnumValueFormula::DateArray(DateArrayFormulas::define_locals(locals, value)) + } + EnumValueFormula::DateTimeArray(value) => { + EnumValueFormula::DateTimeArray(DateTimeArrayFormulas::define_locals(locals, value)) + } + EnumValueFormula::ObjectArray(value) => { + EnumValueFormula::ObjectArray(ObjectArrayFormulas::define_locals(locals, value)) + } + EnumValueFormula::IntConstant(value) => EnumValueFormula::Int( + IntFormulas::define_locals(locals, IntFormulas::value(value)), + ), + EnumValueFormula::StringConstant(value) => EnumValueFormula::String( + StringFormulas::define_locals(locals, StringFormulas::value(value)), + ), + EnumValueFormula::LiteralConstant(value) => EnumValueFormula::String( + StringFormulas::define_locals(locals, StringFormulas::value(value)), + ), + } + } + + pub fn to_bool(self) -> Option { + match self { + EnumValueFormula::Bool(value) => Some(value), + _ => None, + } + } + + pub fn to_int(self) -> Option { + match self { + EnumValueFormula::Int(value) => Some(value), + EnumValueFormula::IntConstant(value) => Some(IntFormulas::value(value)), + _ => None, + } + } + + pub fn to_int_constant(self) -> Option { + match self { + EnumValueFormula::IntConstant(value) => Some(value), + _ => None, + } + } + + pub fn to_decimal(self) -> Option { + match self { + EnumValueFormula::Decimal(value) => Some(value), + EnumValueFormula::Int(value) => Some(DecimalFormulas::from_int(value)), + EnumValueFormula::IntConstant(value) => Some(DecimalFormulas::value( + BigDecimal::from_i64(value).expect("could not convert int to BigDecimal"), + )), + _ => None, + } + } + + pub fn to_string(self) -> Option { + match self { + EnumValueFormula::String(value) => Some(value), + EnumValueFormula::StringConstant(value) => Some(StringFormulas::value(value)), + EnumValueFormula::Int(value) => Some(StringFormulas::from_int(value)), + EnumValueFormula::IntConstant(value) => Some(StringFormulas::value(value.to_string())), + EnumValueFormula::Decimal(value) => Some(StringFormulas::from_decimal(value)), + _ => None, + } + } + + pub fn to_string_constant(self) -> Option { + match self { + EnumValueFormula::StringConstant(value) => Some(value), + _ => None, + } + } + + pub fn to_date(self) -> Option { + match self { + EnumValueFormula::Date(value) => Some(value), + _ => None, + } + } + + pub fn to_datetime(self) -> Option { + match self { + EnumValueFormula::DateTime(value) => Some(value), + _ => None, + } + } + + pub fn to_object(self) -> Option { + match self { + EnumValueFormula::Object(value) => Some(value), + _ => None, + } + } + + pub fn to_bool_array(self) -> Option { + match self { + EnumValueFormula::BoolArray(value) => Some(value), + _ => None, + } + } + + pub fn to_int_array(self) -> Option { + match self { + EnumValueFormula::IntArray(value) => Some(value), + _ => None, + } + } + + pub fn to_decimal_array(self) -> Option { + match self { + EnumValueFormula::DecimalArray(value) => Some(value), + _ => None, + } + } + + pub fn to_string_array(self) -> Option { + match self { + EnumValueFormula::StringArray(value) => Some(value), + _ => None, + } + } + + pub fn to_date_array(self) -> Option { + match self { + EnumValueFormula::DateArray(value) => Some(value), + _ => None, + } + } + + pub fn to_datetime_array(self) -> Option { + match self { + EnumValueFormula::DateTimeArray(value) => Some(value), + _ => None, + } + } + + pub fn to_object_array(self) -> Option { + match self { + EnumValueFormula::ObjectArray(value) => Some(value), + _ => None, + } + } + + pub fn to_literal_constant(self) -> Option { + match self { + EnumValueFormula::LiteralConstant(value) => Some(value), + _ => None, + } + } + + pub fn evaluate(&self, context: &FormulaContext) -> Result { + match self { + EnumValueFormula::Bool(value) => value.evaluate(context).map(Value::Bool), + EnumValueFormula::Int(value) => value.evaluate(context).map(Value::Int), + EnumValueFormula::Decimal(value) => value.evaluate(context).map(Value::Decimal), + EnumValueFormula::String(value) => value.evaluate(context).map(Value::String), + EnumValueFormula::Date(value) => value.evaluate(context).map(Value::Date), + EnumValueFormula::DateTime(value) => value.evaluate(context).map(Value::DateTime), + EnumValueFormula::Object(value) => value.evaluate(context).map(Value::Object), + EnumValueFormula::BoolArray(value) => value.evaluate(context).map(Value::BoolArray), + EnumValueFormula::IntArray(value) => value.evaluate(context).map(Value::IntArray), + EnumValueFormula::DecimalArray(value) => { + value.evaluate(context).map(Value::DecimalArray) + } + EnumValueFormula::StringArray(value) => value.evaluate(context).map(Value::StringArray), + EnumValueFormula::DateArray(value) => value.evaluate(context).map(Value::DateArray), + EnumValueFormula::DateTimeArray(value) => { + value.evaluate(context).map(Value::DateTimeArray) + } + EnumValueFormula::ObjectArray(value) => value.evaluate(context).map(Value::ObjectArray), + EnumValueFormula::IntConstant(value) => Ok(Value::Int(*value)), + EnumValueFormula::StringConstant(value) => Ok(Value::String(value.clone())), + EnumValueFormula::LiteralConstant(value) => Ok(Value::String(value.clone())), + } + } + + pub fn format_to_string(&self) -> String { + match self { + EnumValueFormula::Bool(value) => value.to_string(), + EnumValueFormula::Int(value) => value.to_string(), + EnumValueFormula::Decimal(value) => value.to_string(), + EnumValueFormula::String(value) => value.to_string(), + EnumValueFormula::Date(value) => value.to_string(), + EnumValueFormula::DateTime(value) => value.to_string(), + EnumValueFormula::Object(value) => value.to_string(), + EnumValueFormula::BoolArray(value) => value.to_string(), + EnumValueFormula::IntArray(value) => value.to_string(), + EnumValueFormula::DecimalArray(value) => value.to_string(), + EnumValueFormula::StringArray(value) => value.to_string(), + EnumValueFormula::DateArray(value) => value.to_string(), + EnumValueFormula::DateTimeArray(value) => value.to_string(), + EnumValueFormula::ObjectArray(value) => value.to_string(), + EnumValueFormula::IntConstant(value) => value.to_string(), + EnumValueFormula::StringConstant(value) => value.clone(), + EnumValueFormula::LiteralConstant(value) => value.clone(), + } + } + + pub fn serialize(&self) -> Vec { + let mut writer = FormulaWriter::new(); + self.serialize_to(&mut writer); + writer.into_bytes() + } + + pub fn deserialize(data: &[u8]) -> DeserializedResult { + let mut reader = FormulaReader::new(data); + deserialize_value_formula_from_reader(&mut reader) + } + + pub fn serialize_to(&self, writer: &mut FormulaWriter) { + match self { + EnumValueFormula::Bool(value) => { + writer.write_byte(Type::Bool.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::Int(value) => { + writer.write_byte(Type::Int.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::Decimal(value) => { + writer.write_byte(Type::Decimal.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::String(value) => { + writer.write_byte(Type::String.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::Date(value) => { + writer.write_byte(Type::Date.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::DateTime(value) => { + writer.write_byte(Type::DateTime.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::Object(value) => { + writer.write_byte(Type::Object.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::BoolArray(value) => { + writer.write_byte(Type::BoolArray.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::IntArray(value) => { + writer.write_byte(Type::IntArray.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::DecimalArray(value) => { + writer.write_byte(Type::DecimalArray.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::StringArray(value) => { + writer.write_byte(Type::StringArray.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::DateArray(value) => { + writer.write_byte(Type::DateArray.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::DateTimeArray(value) => { + writer.write_byte(Type::DateTimeArray.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::ObjectArray(value) => { + writer.write_byte(Type::ObjectArray.serialize()); + value.serialize_to(writer); + } + EnumValueFormula::IntConstant(value) => { + writer.write_byte(Type::Int.serialize()); + IntFormulas::value(*value).serialize_to(writer); + } + EnumValueFormula::StringConstant(value) => { + writer.write_byte(Type::String.serialize()); + StringFormulas::value(value.clone()).serialize_to(writer); + } + EnumValueFormula::LiteralConstant(value) => { + writer.write_byte(Type::String.serialize()); + StringFormulas::value(value.clone()).serialize_to(writer); + } + } + } +}