Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
517
Cargo.lock
generated
Normal file
517
Cargo.lock
generated
Normal file
@@ -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"
|
||||||
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@@ -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"
|
||||||
1060
src/array_formula.rs
Normal file
1060
src/array_formula.rs
Normal file
File diff suppressed because it is too large
Load Diff
1532
src/bool_formula.rs
Normal file
1532
src/bool_formula.rs
Normal file
File diff suppressed because it is too large
Load Diff
1272
src/compilation/compiler.rs
Normal file
1272
src/compilation/compiler.rs
Normal file
File diff suppressed because it is too large
Load Diff
172
src/compilation/lexer.rs
Normal file
172
src/compilation/lexer.rs
Normal file
@@ -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<String> {
|
||||||
|
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<Lexer<'a, Token<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Token> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
201
src/compilation/mod.rs
Normal file
201
src/compilation/mod.rs
Normal file
@@ -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<T> = Result<T, CompileError>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum FullType {
|
||||||
|
Basic(Type),
|
||||||
|
Array(Box<FullType>),
|
||||||
|
Object(Arc<dyn ObjectTypeDefinition>),
|
||||||
|
EnumConstant(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FullType {
|
||||||
|
pub fn from_simple(t: Type) -> CompileResult<Self> {
|
||||||
|
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<Arc<dyn ObjectTypeDefinition>> {
|
||||||
|
match self {
|
||||||
|
FullType::Object(t) => Some(t.clone()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_element_type(&self) -> Option<FullType> {
|
||||||
|
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<FullType>;
|
||||||
|
|
||||||
|
fn get_method_headers(&self, name: &str) -> Option<Vec<&MethodHeader>>;
|
||||||
|
|
||||||
|
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<FullType> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_method_headers(&self, _name: &str) -> Option<Vec<&MethodHeader>> {
|
||||||
|
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<TypedFormula>) -> CompileResult<TypedFormula>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InstanceMethod {
|
||||||
|
fn get_header(&self) -> &MethodHeader;
|
||||||
|
fn call(&self, instance: TypedFormula, args: Vec<TypedFormula>) -> CompileResult<TypedFormula>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MethodHeader {
|
||||||
|
pub method_id: String,
|
||||||
|
pub return_type: FullType,
|
||||||
|
pub argument_types: Vec<FullType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MethodHeader {
|
||||||
|
pub fn new(method_id: String, return_type: Type, argument_types: Vec<Type>) -> 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;
|
||||||
373
src/compilation/parser.rs
Normal file
373
src/compilation/parser.rs
Normal file
@@ -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<ExpressionTree>,
|
||||||
|
then: Box<ExpressionTree>,
|
||||||
|
else_: Box<ExpressionTree>,
|
||||||
|
},
|
||||||
|
UnaryOp {
|
||||||
|
op: UnaryOp,
|
||||||
|
value: Box<ExpressionTree>,
|
||||||
|
},
|
||||||
|
BinaryOp {
|
||||||
|
op: BinaryOp,
|
||||||
|
left: Box<ExpressionTree>,
|
||||||
|
right: Box<ExpressionTree>,
|
||||||
|
},
|
||||||
|
GetField {
|
||||||
|
object: Box<ExpressionTree>,
|
||||||
|
field: String,
|
||||||
|
},
|
||||||
|
Call {
|
||||||
|
target: Box<ExpressionTree>,
|
||||||
|
args: Vec<Box<ExpressionTree>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExpressionTree {}
|
||||||
|
|
||||||
|
pub struct ParsedExpression {
|
||||||
|
pub formula: ExpressionTree,
|
||||||
|
pub locals: Vec<ParsedLocalDefinition>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ParsedLocalDefinition {
|
||||||
|
pub name: String,
|
||||||
|
pub value: ExpressionTree,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(formula: &str) -> CompileResult<ParsedExpression> {
|
||||||
|
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<ParsedLocalDefinition> {
|
||||||
|
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<ExpressionTree> {
|
||||||
|
parse_conditional_expression(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_conditional_expression(stream: &mut TokenStream) -> CompileResult<ExpressionTree> {
|
||||||
|
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<ExpressionTree> {
|
||||||
|
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<ExpressionTree> {
|
||||||
|
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<ExpressionTree> {
|
||||||
|
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<ExpressionTree> {
|
||||||
|
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<ExpressionTree> {
|
||||||
|
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<ExpressionTree> {
|
||||||
|
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<ExpressionTree> {
|
||||||
|
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<ExpressionTree> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
195
src/compilation/tests.rs
Normal file
195
src/compilation/tests.rs
Normal file
@@ -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<Value, ExecutionError> {
|
||||||
|
match field {
|
||||||
|
"ivalue" => Ok(Value::Int(self.ivalue)),
|
||||||
|
_ => Err(ExecutionError::NoSuchField {
|
||||||
|
typename: "TestRoot".into(),
|
||||||
|
field: field.into(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&self, method: &str, _args: Vec<Value>) -> Result<Value, ExecutionError> {
|
||||||
|
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<crate::compilation::FullType> {
|
||||||
|
match name {
|
||||||
|
"ivalue" => Some(crate::compilation::FullType::Basic(crate::types::Type::Int)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_method_headers(&self, _name: &str) -> Option<Vec<&crate::compilation::MethodHeader>> {
|
||||||
|
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());
|
||||||
|
}
|
||||||
36
src/compilation/typed_formula.rs
Normal file
36
src/compilation/typed_formula.rs
Normal file
@@ -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<TypedFormula> {
|
||||||
|
let type_ = FullType::from_simple(value.type_())?;
|
||||||
|
Ok(TypedFormula { type_, value })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from<T: AnyType>(value: Box<dyn Formula<T::T>>) -> CompileResult<TypedFormula> {
|
||||||
|
let type_ = FullType::from_simple(T::TYPE)?;
|
||||||
|
Ok(TypedFormula {
|
||||||
|
type_,
|
||||||
|
value: T::to_enum_formula(value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn require_bool(self) -> CompileResult<BoolFormula> {
|
||||||
|
self.value
|
||||||
|
.to_bool()
|
||||||
|
.ok_or(CompileError::InvalidArgumentType {
|
||||||
|
expected: Type::Bool,
|
||||||
|
found: self.type_.simplified(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
362
src/date_formula.rs
Normal file
362
src/date_formula.rs
Normal file
@@ -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<DateFormula> {
|
||||||
|
let mut reader = FormulaReader::new(bytes);
|
||||||
|
Self::from_reader(&mut reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult<DateFormula> {
|
||||||
|
let operator = reader.read_byte()?;
|
||||||
|
match operator {
|
||||||
|
GENERIC_OP_INVALID => InvalidFormula::<DateType>::deserialize(reader),
|
||||||
|
GENERIC_OP_DEFINE_LOCALS => DefineLocals::<DateType>::deserialize(reader),
|
||||||
|
GENERIC_OP_LOCAL => LocalVariable::<DateType>::deserialize(reader),
|
||||||
|
GENERIC_OP_OBJECT_FIELD => ObjectField::<DateType>::deserialize(reader),
|
||||||
|
GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::<DateType>::deserialize(reader),
|
||||||
|
GENERIC_OP_ARRAY_ELEMENT => ArrayElement::<DateType>::deserialize(reader),
|
||||||
|
GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT => {
|
||||||
|
ArrayElementOrDefault::<DateType>::deserialize(reader)
|
||||||
|
}
|
||||||
|
GENERIC_OP_ARRAY_FIRST => ArrayFirst::<DateType>::deserialize(reader),
|
||||||
|
GENERIC_OP_ARRAY_LAST => ArrayLast::<DateType>::deserialize(reader),
|
||||||
|
GENERIC_OP_TERNARY => Ternary::<DateType>::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::<DateType>::new(formula)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn define_locals(locals: Vec<LocalDefinition>, formula: DateFormula) -> DateFormula {
|
||||||
|
DefineLocals::<DateType>::new(locals, formula)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local(index: u32) -> DateFormula {
|
||||||
|
LocalVariable::<DateType>::new(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn object_field(object: ObjectFormula, field: String) -> DateFormula {
|
||||||
|
ObjectField::<DateType>::new(object, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn object_method_call(
|
||||||
|
object: ObjectFormula,
|
||||||
|
method: String,
|
||||||
|
arguments: Vec<EnumValueFormula>,
|
||||||
|
) -> DateFormula {
|
||||||
|
ObjectMethodCall::<DateType>::new(object, method, arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_element(array: DateArrayFormula, index: IntFormula) -> DateFormula {
|
||||||
|
ArrayElement::<DateType>::new(array, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_element_or_default(
|
||||||
|
array: DateArrayFormula,
|
||||||
|
index: IntFormula,
|
||||||
|
default: DateFormula,
|
||||||
|
) -> DateFormula {
|
||||||
|
ArrayElementOrDefault::<DateType>::new(array, index, default)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_first(array: DateArrayFormula) -> DateFormula {
|
||||||
|
ArrayFirst::<DateType>::new(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_last(array: DateArrayFormula) -> DateFormula {
|
||||||
|
ArrayLast::<DateType>::new(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ternary(condition: BoolFormula, then: DateFormula, else_: DateFormula) -> DateFormula {
|
||||||
|
Ternary::<DateType>::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<DateFormula> {
|
||||||
|
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<NaiveDate> for DateConstant {
|
||||||
|
fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult<NaiveDate> {
|
||||||
|
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<DateFormula> {
|
||||||
|
let value = StringFormulas::from_reader(reader)?;
|
||||||
|
Ok(Self::new(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<NaiveDate> for DateFromIsoString {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<NaiveDate> {
|
||||||
|
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<DateFormula> {
|
||||||
|
let date = DateFormulas::from_reader(reader)?;
|
||||||
|
let days = IntFormulas::from_reader(reader)?;
|
||||||
|
Ok(Self::new(date, days))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<NaiveDate> for DateAddDays {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> Result<NaiveDate, ExecutionError> {
|
||||||
|
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<DateFormula> {
|
||||||
|
let date = DateFormulas::from_reader(reader)?;
|
||||||
|
let months = IntFormulas::from_reader(reader)?;
|
||||||
|
Ok(Self::new(date, months))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<NaiveDate> for DateAddMonths {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<NaiveDate> {
|
||||||
|
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<DateFormula> {
|
||||||
|
let date = DateFormulas::from_reader(reader)?;
|
||||||
|
let years = IntFormulas::from_reader(reader)?;
|
||||||
|
Ok(Self::new(date, years))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<NaiveDate> for DateAddYears {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<NaiveDate> {
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
414
src/datetime_formula.rs
Normal file
414
src/datetime_formula.rs
Normal file
@@ -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<DateTimeFormula> {
|
||||||
|
let mut reader = FormulaReader::new(bytes);
|
||||||
|
Self::from_reader(&mut reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_reader(reader: &mut FormulaReader) -> DeserializedResult<DateTimeFormula> {
|
||||||
|
let operator = reader.read_byte()?;
|
||||||
|
match operator {
|
||||||
|
GENERIC_OP_INVALID => InvalidFormula::<DateTimeType>::deserialize(reader),
|
||||||
|
GENERIC_OP_DEFINE_LOCALS => DefineLocals::<DateTimeType>::deserialize(reader),
|
||||||
|
GENERIC_OP_LOCAL => LocalVariable::<DateTimeType>::deserialize(reader),
|
||||||
|
GENERIC_OP_OBJECT_FIELD => ObjectField::<DateTimeType>::deserialize(reader),
|
||||||
|
GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::<DateTimeType>::deserialize(reader),
|
||||||
|
GENERIC_OP_ARRAY_ELEMENT => ArrayElement::<DateTimeType>::deserialize(reader),
|
||||||
|
GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT => {
|
||||||
|
ArrayElementOrDefault::<DateTimeType>::deserialize(reader)
|
||||||
|
}
|
||||||
|
GENERIC_OP_ARRAY_FIRST => ArrayFirst::<DateTimeType>::deserialize(reader),
|
||||||
|
GENERIC_OP_ARRAY_LAST => ArrayLast::<DateTimeType>::deserialize(reader),
|
||||||
|
GENERIC_OP_TERNARY => Ternary::<DateTimeType>::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::<DateTimeType>::new(formula)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn define_locals(
|
||||||
|
locals: Vec<LocalDefinition>,
|
||||||
|
formula: DateTimeFormula,
|
||||||
|
) -> DateTimeFormula {
|
||||||
|
DefineLocals::<DateTimeType>::new(locals, formula)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local(index: u32) -> DateTimeFormula {
|
||||||
|
LocalVariable::<DateTimeType>::new(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn object_field(object: ObjectFormula, field: String) -> DateTimeFormula {
|
||||||
|
ObjectField::<DateTimeType>::new(object, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn object_method_call(
|
||||||
|
object: ObjectFormula,
|
||||||
|
method: String,
|
||||||
|
arguments: Vec<EnumValueFormula>,
|
||||||
|
) -> DateTimeFormula {
|
||||||
|
ObjectMethodCall::<DateTimeType>::new(object, method, arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_element(array: DateTimeArrayFormula, index: IntFormula) -> DateTimeFormula {
|
||||||
|
ArrayElement::<DateTimeType>::new(array, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_element_or_default(
|
||||||
|
array: DateTimeArrayFormula,
|
||||||
|
index: IntFormula,
|
||||||
|
default: DateTimeFormula,
|
||||||
|
) -> DateTimeFormula {
|
||||||
|
ArrayElementOrDefault::<DateTimeType>::new(array, index, default)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_first(array: DateTimeArrayFormula) -> DateTimeFormula {
|
||||||
|
ArrayFirst::<DateTimeType>::new(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_last(array: DateTimeArrayFormula) -> DateTimeFormula {
|
||||||
|
ArrayLast::<DateTimeType>::new(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ternary(
|
||||||
|
condition: BoolFormula,
|
||||||
|
then: DateTimeFormula,
|
||||||
|
else_: DateTimeFormula,
|
||||||
|
) -> DateTimeFormula {
|
||||||
|
Ternary::<DateTimeType>::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<DateTimeFormula> {
|
||||||
|
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<NaiveDateTime> for DateTimeConstant {
|
||||||
|
fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult<NaiveDateTime> {
|
||||||
|
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<DateTimeFormula> {
|
||||||
|
let value = StringFormulas::from_reader(reader)?;
|
||||||
|
Ok(Self::new(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<NaiveDateTime> for DateTimeFromIsoString {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<NaiveDateTime> {
|
||||||
|
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<DateTimeFormula> {
|
||||||
|
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<NaiveDateTime> for DateWithTime {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<NaiveDateTime> {
|
||||||
|
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<Self> {
|
||||||
|
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<DateTimeFormula> {
|
||||||
|
let date = DateTimeFormulas::from_reader(reader)?;
|
||||||
|
let from = FormulaTimeZone::deserialize(reader)?;
|
||||||
|
let to = FormulaTimeZone::deserialize(reader)?;
|
||||||
|
Ok(Self::new(date, from, to))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<NaiveDateTime> for DateTimeShiftTimezone {
|
||||||
|
fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult<NaiveDateTime> {
|
||||||
|
/*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!()
|
||||||
|
}
|
||||||
|
}
|
||||||
1132
src/decimal_formula.rs
Normal file
1132
src/decimal_formula.rs
Normal file
File diff suppressed because it is too large
Load Diff
88
src/formula_reader.rs
Normal file
88
src/formula_reader.rs
Normal file
@@ -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<u8> {
|
||||||
|
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<u32> {
|
||||||
|
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<i32> {
|
||||||
|
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<u64> {
|
||||||
|
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<i64> {
|
||||||
|
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<String> {
|
||||||
|
let bytes = self.read_bytes()?;
|
||||||
|
String::from_utf8(bytes.to_vec()).map_err(|_| DeserializationError::InvalidUtf8)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_char(&mut self) -> DeserializedResult<char> {
|
||||||
|
let separator =
|
||||||
|
char::try_from(self.read_u32()?).map_err(|_| DeserializationError::InvalidCharacter)?;
|
||||||
|
Ok(separator)
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/formula_string.rs
Normal file
77
src/formula_string.rs
Normal file
@@ -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<A, F: Fn(String) -> String>(
|
||||||
|
operand: &dyn Formula<A>,
|
||||||
|
priority: OperatorPriority,
|
||||||
|
wrap: F,
|
||||||
|
) -> Self {
|
||||||
|
let operand = operand.to_formula_string();
|
||||||
|
Self::new(wrap(operand.wrap(priority)), priority)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn binary<A, B, F: Fn(String, String) -> String>(
|
||||||
|
left: &dyn Formula<A>,
|
||||||
|
right: &dyn Formula<B>,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/formula_writer.rs
Normal file
75
src/formula_writer.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
pub struct FormulaWriter {
|
||||||
|
bytes: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<u8> {
|
||||||
|
self.bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
539
src/generic.rs
Normal file
539
src/generic.rs
Normal file
@@ -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<T: AnyType> {
|
||||||
|
content: String,
|
||||||
|
phantom: std::marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AnyType> InvalidFormula<T> {
|
||||||
|
pub fn new(content: String) -> Box<dyn Formula<T::T>> {
|
||||||
|
Box::new(Self {
|
||||||
|
content,
|
||||||
|
phantom: std::marker::PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult<Box<dyn Formula<T::T>>> {
|
||||||
|
let content = reader.read_string()?;
|
||||||
|
Ok(Self::new(content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AnyType> Formula<T::T> for InvalidFormula<T> {
|
||||||
|
fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult<T::T> {
|
||||||
|
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<T: AnyType> {
|
||||||
|
locals: Vec<LocalDefinition>,
|
||||||
|
value: Box<dyn Formula<T::T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AnyType> DefineLocals<T> {
|
||||||
|
pub fn new(
|
||||||
|
locals: Vec<LocalDefinition>,
|
||||||
|
value: Box<dyn Formula<T::T>>,
|
||||||
|
) -> Box<dyn Formula<T::T>> {
|
||||||
|
Box::new(Self { locals, value })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult<Box<dyn Formula<T::T>>> {
|
||||||
|
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<T: AnyType> Formula<T::T> for DefineLocals<T> {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<T::T> {
|
||||||
|
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::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
FormulaString::new(
|
||||||
|
format!("{} where {}", result, variables),
|
||||||
|
OperatorPriority::None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LocalVariable<T: AnyType> {
|
||||||
|
local: u32,
|
||||||
|
phantom: std::marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AnyType> LocalVariable<T> {
|
||||||
|
pub fn new(local: u32) -> Box<dyn Formula<T::T>> {
|
||||||
|
Box::new(Self {
|
||||||
|
local,
|
||||||
|
phantom: std::marker::PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult<Box<dyn Formula<T::T>>> {
|
||||||
|
let local = reader.read_u32()?;
|
||||||
|
Ok(Self::new(local))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AnyType> Formula<T::T> for LocalVariable<T> {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<T::T> {
|
||||||
|
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<T: AnyType> {
|
||||||
|
object: ObjectFormula,
|
||||||
|
field: String,
|
||||||
|
phantom: std::marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AnyType> ObjectField<T> {
|
||||||
|
pub fn new(object: ObjectFormula, field: String) -> Box<dyn Formula<T::T>> {
|
||||||
|
Box::new(Self {
|
||||||
|
object,
|
||||||
|
field,
|
||||||
|
phantom: std::marker::PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult<Box<dyn Formula<T::T>>> {
|
||||||
|
let object = ObjectFormulas::from_reader(reader)?;
|
||||||
|
let field = reader.read_string()?;
|
||||||
|
Ok(Self::new(object, field))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AnyType> Formula<T::T> for ObjectField<T> {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<T::T> {
|
||||||
|
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<T: AnyType> {
|
||||||
|
object: ObjectFormula,
|
||||||
|
method: String,
|
||||||
|
arguments: Vec<EnumValueFormula>,
|
||||||
|
phantom: std::marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AnyType> ObjectMethodCall<T> {
|
||||||
|
pub fn new(
|
||||||
|
object: ObjectFormula,
|
||||||
|
method: String,
|
||||||
|
arguments: Vec<EnumValueFormula>,
|
||||||
|
) -> Box<dyn Formula<T::T>> {
|
||||||
|
Box::new(Self {
|
||||||
|
object,
|
||||||
|
method,
|
||||||
|
arguments,
|
||||||
|
phantom: std::marker::PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult<Box<dyn Formula<T::T>>> {
|
||||||
|
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<T: AnyType> Formula<T::T> for ObjectMethodCall<T> {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<T::T> {
|
||||||
|
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::<Vec<_>>()
|
||||||
|
.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<T: ScalarType> {
|
||||||
|
array: Box<dyn Formula<Vec<T::T>>>,
|
||||||
|
index: IntFormula,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ScalarType> ArrayElement<T> {
|
||||||
|
pub fn new(array: Box<dyn Formula<Vec<T::T>>>, index: IntFormula) -> Box<dyn Formula<T::T>> {
|
||||||
|
Box::new(Self { array, index })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult<Box<dyn Formula<T::T>>> {
|
||||||
|
let array = T::ARRAY::formula_from_reader(reader)?;
|
||||||
|
let index = IntFormulas::from_reader(reader)?;
|
||||||
|
Ok(Box::new(Self { array, index }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ScalarType> Formula<T::T> for ArrayElement<T> {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<T::T> {
|
||||||
|
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<T: ScalarType> {
|
||||||
|
array: Box<dyn Formula<Vec<T::T>>>,
|
||||||
|
index: IntFormula,
|
||||||
|
default: Box<dyn Formula<T::T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ScalarType> ArrayElementOrDefault<T> {
|
||||||
|
pub fn new(
|
||||||
|
array: Box<dyn Formula<Vec<T::T>>>,
|
||||||
|
index: IntFormula,
|
||||||
|
default: Box<dyn Formula<T::T>>,
|
||||||
|
) -> Box<dyn Formula<T::T>> {
|
||||||
|
Box::new(Self {
|
||||||
|
array,
|
||||||
|
index,
|
||||||
|
default,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult<Box<dyn Formula<T::T>>> {
|
||||||
|
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<T: ScalarType> Formula<T::T> for ArrayElementOrDefault<T> {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<T::T> {
|
||||||
|
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<T: ScalarType> {
|
||||||
|
array: Box<dyn Formula<Vec<T::T>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ScalarType> ArrayFirst<T> {
|
||||||
|
pub fn new(array: Box<dyn Formula<Vec<T::T>>>) -> Box<dyn Formula<T::T>> {
|
||||||
|
Box::new(Self { array })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult<Box<dyn Formula<T::T>>> {
|
||||||
|
let array = T::ARRAY::formula_from_reader(reader)?;
|
||||||
|
Ok(Box::new(Self { array }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ScalarType> Formula<T::T> for ArrayFirst<T> {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<T::T> {
|
||||||
|
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<T: ScalarType> {
|
||||||
|
array: Box<dyn Formula<Vec<T::T>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ScalarType> ArrayLast<T> {
|
||||||
|
pub fn new(array: Box<dyn Formula<Vec<T::T>>>) -> Box<dyn Formula<T::T>> {
|
||||||
|
Box::new(Self { array })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult<Box<dyn Formula<T::T>>> {
|
||||||
|
let array = T::ARRAY::formula_from_reader(reader)?;
|
||||||
|
Ok(Box::new(Self { array }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ScalarType> Formula<T::T> for ArrayLast<T> {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<T::T> {
|
||||||
|
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<T: AnyType> {
|
||||||
|
condition: BoolFormula,
|
||||||
|
true_value: Box<dyn Formula<T::T>>,
|
||||||
|
false_value: Box<dyn Formula<T::T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AnyType> Ternary<T> {
|
||||||
|
pub fn new(
|
||||||
|
condition: BoolFormula,
|
||||||
|
true_value: Box<dyn Formula<T::T>>,
|
||||||
|
false_value: Box<dyn Formula<T::T>>,
|
||||||
|
) -> Box<dyn Formula<T::T>> {
|
||||||
|
Box::new(Self {
|
||||||
|
condition,
|
||||||
|
true_value,
|
||||||
|
false_value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(reader: &mut FormulaReader) -> DeserializedResult<Box<dyn Formula<T::T>>> {
|
||||||
|
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<T: AnyType> Formula<T::T> for Ternary<T> {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<T::T> {
|
||||||
|
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<Vec<Value>> {
|
||||||
|
let mut result = Vec::with_capacity(arguments.len());
|
||||||
|
for argument in arguments {
|
||||||
|
result.push(argument.evaluate(context)?);
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
1698
src/int_formula.rs
Normal file
1698
src/int_formula.rs
Normal file
File diff suppressed because it is too large
Load Diff
185
src/lib.rs
Normal file
185
src/lib.rs
Normal file
@@ -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<dyn FormulaObject>,
|
||||||
|
pub locals: Vec<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormulaContext {
|
||||||
|
pub fn new(root: Arc<dyn FormulaObject>) -> 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<Value> {
|
||||||
|
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<T> = Result<T, ExecutionError>;
|
||||||
|
|
||||||
|
pub trait Formula<T> {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<T>;
|
||||||
|
|
||||||
|
fn serialize_to(&self, writer: &mut FormulaWriter);
|
||||||
|
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
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<dyn Formula<bool>>;
|
||||||
|
pub type IntFormula = Box<dyn Formula<i64>>;
|
||||||
|
pub type DecimalFormula = Box<dyn Formula<BigDecimal>>;
|
||||||
|
pub type StringFormula = Box<dyn Formula<String>>;
|
||||||
|
pub type DateFormula = Box<dyn Formula<NaiveDate>>;
|
||||||
|
pub type DateTimeFormula = Box<dyn Formula<chrono::NaiveDateTime>>;
|
||||||
|
pub type ObjectFormula = Box<dyn Formula<Arc<dyn FormulaObject>>>;
|
||||||
|
pub type BoolArrayFormula = Box<dyn Formula<Vec<bool>>>;
|
||||||
|
pub type IntArrayFormula = Box<dyn Formula<Vec<i64>>>;
|
||||||
|
pub type DecimalArrayFormula = Box<dyn Formula<Vec<BigDecimal>>>;
|
||||||
|
pub type StringArrayFormula = Box<dyn Formula<Vec<String>>>;
|
||||||
|
pub type DateArrayFormula = Box<dyn Formula<Vec<NaiveDate>>>;
|
||||||
|
pub type DateTimeArrayFormula = Box<dyn Formula<Vec<chrono::NaiveDateTime>>>;
|
||||||
|
pub type ObjectArrayFormula = Box<dyn Formula<Vec<Arc<dyn FormulaObject>>>>;
|
||||||
|
pub type ValueFormula = Box<dyn Formula<Value>>;
|
||||||
|
|
||||||
|
#[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<T> = Result<T, DeserializationError>;
|
||||||
122
src/object.rs
Normal file
122
src/object.rs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::{value::Value, ExecutionError};
|
||||||
|
|
||||||
|
pub trait FormulaObject: Send + Sync {
|
||||||
|
fn get(&self, field: &str) -> Result<Value, ExecutionError>;
|
||||||
|
fn call(&self, method: &str, args: Vec<Value>) -> Result<Value, ExecutionError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EmptyObject;
|
||||||
|
|
||||||
|
impl EmptyObject {
|
||||||
|
pub fn new() -> Arc<dyn FormulaObject> {
|
||||||
|
Arc::new(Self {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormulaObject for EmptyObject {
|
||||||
|
fn get(&self, field: &str) -> Result<Value, ExecutionError> {
|
||||||
|
Err(ExecutionError::NoSuchField {
|
||||||
|
typename: "{}".into(),
|
||||||
|
field: field.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&self, method: &str, _args: Vec<Value>) -> Result<Value, ExecutionError> {
|
||||||
|
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<dyn FormulaObject> {
|
||||||
|
Arc::new(Self { id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl FormulaObject for TestObject {
|
||||||
|
fn get(&self, field: &str) -> Result<Value, ExecutionError> {
|
||||||
|
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<Value>) -> Result<Value, ExecutionError> {
|
||||||
|
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()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
156
src/object_formula.rs
Normal file
156
src/object_formula.rs
Normal file
@@ -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<Box<dyn Formula<Arc<dyn FormulaObject>>>, DeserializationError> {
|
||||||
|
let mut reader = FormulaReader::new(bytes);
|
||||||
|
Self::from_reader(&mut reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_reader(
|
||||||
|
reader: &mut FormulaReader,
|
||||||
|
) -> Result<Box<dyn Formula<Arc<dyn FormulaObject>>>, DeserializationError> {
|
||||||
|
let operator = reader.read_byte()?;
|
||||||
|
match operator {
|
||||||
|
GENERIC_OP_INVALID => InvalidFormula::<ObjectType>::deserialize(reader),
|
||||||
|
GENERIC_OP_DEFINE_LOCALS => DefineLocals::<ObjectType>::deserialize(reader),
|
||||||
|
GENERIC_OP_LOCAL => LocalVariable::<ObjectType>::deserialize(reader),
|
||||||
|
GENERIC_OP_OBJECT_FIELD => ObjectField::<ObjectType>::deserialize(reader),
|
||||||
|
GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::<ObjectType>::deserialize(reader),
|
||||||
|
GENERIC_OP_ARRAY_ELEMENT => ArrayElement::<ObjectType>::deserialize(reader),
|
||||||
|
GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT => {
|
||||||
|
ArrayElementOrDefault::<ObjectType>::deserialize(reader)
|
||||||
|
}
|
||||||
|
GENERIC_OP_ARRAY_FIRST => ArrayFirst::<ObjectType>::deserialize(reader),
|
||||||
|
GENERIC_OP_ARRAY_LAST => ArrayLast::<ObjectType>::deserialize(reader),
|
||||||
|
GENERIC_OP_TERNARY => Ternary::<ObjectType>::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<LocalDefinition>, formula: ObjectFormula) -> ObjectFormula {
|
||||||
|
DefineLocals::<ObjectType>::new(locals, formula)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local(index: u32) -> ObjectFormula {
|
||||||
|
LocalVariable::<ObjectType>::new(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn object_field(object: ObjectFormula, name: String) -> ObjectFormula {
|
||||||
|
ObjectField::<ObjectType>::new(object, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn object_method_call(
|
||||||
|
object: ObjectFormula,
|
||||||
|
name: String,
|
||||||
|
arguments: Vec<EnumValueFormula>,
|
||||||
|
) -> ObjectFormula {
|
||||||
|
ObjectMethodCall::<ObjectType>::new(object, name, arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_element(array: ObjectArrayFormula, index: IntFormula) -> ObjectFormula {
|
||||||
|
ArrayElement::<ObjectType>::new(array, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_element_or_default(
|
||||||
|
array: ObjectArrayFormula,
|
||||||
|
index: IntFormula,
|
||||||
|
default: ObjectFormula,
|
||||||
|
) -> ObjectFormula {
|
||||||
|
ArrayElementOrDefault::<ObjectType>::new(array, index, default)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_first(array: ObjectArrayFormula) -> ObjectFormula {
|
||||||
|
ArrayFirst::<ObjectType>::new(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_last(array: ObjectArrayFormula) -> ObjectFormula {
|
||||||
|
ArrayLast::<ObjectType>::new(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ternary(
|
||||||
|
condition: BoolFormula,
|
||||||
|
if_true: ObjectFormula,
|
||||||
|
if_false: ObjectFormula,
|
||||||
|
) -> ObjectFormula {
|
||||||
|
Ternary::<ObjectType>::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<Arc<dyn FormulaObject>> for ObjectRoot {
|
||||||
|
fn evaluate(&self, context: &crate::FormulaContext) -> ExecutionResult<Arc<dyn FormulaObject>> {
|
||||||
|
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<Arc<dyn FormulaObject>> for ObjectEmpty {
|
||||||
|
fn evaluate(
|
||||||
|
&self,
|
||||||
|
_context: &crate::FormulaContext,
|
||||||
|
) -> ExecutionResult<Arc<dyn FormulaObject>> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
817
src/string_formula.rs
Normal file
817
src/string_formula.rs
Normal file
@@ -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<Box<dyn Formula<String>>, DeserializationError> {
|
||||||
|
let mut reader = FormulaReader::new(bytes);
|
||||||
|
Self::from_reader(&mut reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_reader(
|
||||||
|
reader: &mut FormulaReader,
|
||||||
|
) -> Result<Box<dyn Formula<String>>, DeserializationError> {
|
||||||
|
let operator = reader.read_byte()?;
|
||||||
|
match operator {
|
||||||
|
GENERIC_OP_INVALID => InvalidFormula::<StringType>::deserialize(reader),
|
||||||
|
GENERIC_OP_DEFINE_LOCALS => DefineLocals::<StringType>::deserialize(reader),
|
||||||
|
GENERIC_OP_LOCAL => LocalVariable::<StringType>::deserialize(reader),
|
||||||
|
GENERIC_OP_OBJECT_FIELD => ObjectField::<StringType>::deserialize(reader),
|
||||||
|
GENERIC_OP_OBJECT_METHOD_CALL => ObjectMethodCall::<StringType>::deserialize(reader),
|
||||||
|
GENERIC_OP_ARRAY_ELEMENT => ArrayElement::<StringType>::deserialize(reader),
|
||||||
|
GENERIC_OP_ARRAY_ELEMENT_OR_DEFAULT => {
|
||||||
|
ArrayElementOrDefault::<StringType>::deserialize(reader)
|
||||||
|
}
|
||||||
|
GENERIC_OP_ARRAY_FIRST => ArrayFirst::<StringType>::deserialize(reader),
|
||||||
|
GENERIC_OP_ARRAY_LAST => ArrayLast::<StringType>::deserialize(reader),
|
||||||
|
GENERIC_OP_TERNARY => Ternary::<StringType>::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<LocalDefinition>, body: StringFormula) -> StringFormula {
|
||||||
|
DefineLocals::<StringType>::new(locals, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local(index: u32) -> StringFormula {
|
||||||
|
LocalVariable::<StringType>::new(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn object_field(object: ObjectFormula, field: String) -> StringFormula {
|
||||||
|
ObjectField::<StringType>::new(object, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn object_method_call(
|
||||||
|
object: ObjectFormula,
|
||||||
|
method: String,
|
||||||
|
arguments: Vec<EnumValueFormula>,
|
||||||
|
) -> StringFormula {
|
||||||
|
ObjectMethodCall::<StringType>::new(object, method, arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_element(array: StringArrayFormula, index: IntFormula) -> StringFormula {
|
||||||
|
ArrayElement::<StringType>::new(array, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_element_or_default(
|
||||||
|
array: StringArrayFormula,
|
||||||
|
index: IntFormula,
|
||||||
|
default: StringFormula,
|
||||||
|
) -> StringFormula {
|
||||||
|
ArrayElementOrDefault::<StringType>::new(array, index, default)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_first(array: StringArrayFormula) -> StringFormula {
|
||||||
|
ArrayFirst::<StringType>::new(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array_last(array: StringArrayFormula) -> StringFormula {
|
||||||
|
ArrayLast::<StringType>::new(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ternary(
|
||||||
|
condition: BoolFormula,
|
||||||
|
left: StringFormula,
|
||||||
|
right: StringFormula,
|
||||||
|
) -> StringFormula {
|
||||||
|
Ternary::<StringType>::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<String> for StringEmpty {
|
||||||
|
fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
let value = reader.read_string()?;
|
||||||
|
Ok(Self::new(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<String> for StringValue {
|
||||||
|
fn evaluate(&self, _context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
let left = StringFormulas::from_reader(reader)?;
|
||||||
|
let right = StringFormulas::from_reader(reader)?;
|
||||||
|
Ok(Self::new(left, right))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<String> for StringConcat {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
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<String> for StringSubstring {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
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<String> for StringLeftPad {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
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<String> for StringRightPad {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
let string = StringFormulas::from_reader(reader)?;
|
||||||
|
Ok(Self::new(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<String> for StringTrim {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
let string = StringFormulas::from_reader(reader)?;
|
||||||
|
Ok(Self::new(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<String> for StringToLower {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
let string = StringFormulas::from_reader(reader)?;
|
||||||
|
Ok(Self::new(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<String> for StringToUpper {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
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<String> for StringTernary {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
let operand = BoolFormulas::from_reader(reader)?;
|
||||||
|
Ok(Self::new(operand))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<String> for StringFromBool {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
let operand = IntFormulas::from_reader(reader)?;
|
||||||
|
Ok(Self::new(operand))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<String> for StringFromInt {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
let operand = DecimalFormulas::from_reader(reader)?;
|
||||||
|
Ok(Self::new(operand))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<String> for StringFromDecimal {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
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<String> for StringExtractRegex {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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<StringFormula> {
|
||||||
|
let values = StringArrayFormulas::from_reader(reader)?;
|
||||||
|
let separator = reader.read_string()?;
|
||||||
|
Ok(Self::new(values, separator))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formula<String> for StringJoin {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> ExecutionResult<String> {
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/string_utils.rs
Normal file
12
src/string_utils.rs
Normal file
@@ -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("\"", "\\\""))
|
||||||
|
}
|
||||||
511
src/types.rs
Normal file
511
src/types.rs
Normal file
@@ -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<Type, DeserializationError> {
|
||||||
|
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<Type> {
|
||||||
|
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<Box<dyn Formula<Self::T>>>;
|
||||||
|
|
||||||
|
fn cast_to_value(value: Self::T) -> Value;
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T>;
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>>;
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> EnumValueFormula;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ScalarType: AnyType {
|
||||||
|
type ARRAY: AnyType<T = Vec<Self::T>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<BoolFormula> {
|
||||||
|
BoolFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: bool) -> Value {
|
||||||
|
Value::Bool(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_bool()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_bool()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> EnumValueFormula {
|
||||||
|
EnumValueFormula::Bool(formula)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyType for BoolArrayType {
|
||||||
|
type T = Vec<bool>;
|
||||||
|
const TYPE: Type = Type::BoolArray;
|
||||||
|
|
||||||
|
fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult<BoolArrayFormula> {
|
||||||
|
BoolArrayFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: Vec<bool>) -> Value {
|
||||||
|
Value::BoolArray(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_bool_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_bool_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> 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<IntFormula> {
|
||||||
|
IntFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: i64) -> Value {
|
||||||
|
Value::Int(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_int()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_int()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> EnumValueFormula {
|
||||||
|
EnumValueFormula::Int(formula)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyType for IntArrayType {
|
||||||
|
type T = Vec<i64>;
|
||||||
|
const TYPE: Type = Type::IntArray;
|
||||||
|
|
||||||
|
fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult<IntArrayFormula> {
|
||||||
|
IntArrayFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: Vec<i64>) -> Value {
|
||||||
|
Value::IntArray(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_int_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_int_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> 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<DecimalFormula> {
|
||||||
|
DecimalFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: BigDecimal) -> Value {
|
||||||
|
Value::Decimal(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_decimal()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_decimal()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> EnumValueFormula {
|
||||||
|
EnumValueFormula::Decimal(formula)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyType for DecimalArrayType {
|
||||||
|
type T = Vec<BigDecimal>;
|
||||||
|
const TYPE: Type = Type::DecimalArray;
|
||||||
|
|
||||||
|
fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult<DecimalArrayFormula> {
|
||||||
|
DecimalArrayFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: Vec<BigDecimal>) -> Value {
|
||||||
|
Value::DecimalArray(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_decimal_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_decimal_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> 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<StringFormula> {
|
||||||
|
StringFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: String) -> Value {
|
||||||
|
Value::String(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> EnumValueFormula {
|
||||||
|
EnumValueFormula::String(formula)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyType for StringArrayType {
|
||||||
|
type T = Vec<String>;
|
||||||
|
const TYPE: Type = Type::StringArray;
|
||||||
|
|
||||||
|
fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult<StringArrayFormula> {
|
||||||
|
StringArrayFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: Vec<String>) -> Value {
|
||||||
|
Value::StringArray(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_string_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_string_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> 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<DateFormula> {
|
||||||
|
DateFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: NaiveDate) -> Value {
|
||||||
|
Value::Date(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_date()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_date()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> EnumValueFormula {
|
||||||
|
EnumValueFormula::Date(formula)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyType for DateArrayType {
|
||||||
|
type T = Vec<NaiveDate>;
|
||||||
|
const TYPE: Type = Type::DateArray;
|
||||||
|
|
||||||
|
fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult<DateArrayFormula> {
|
||||||
|
DateArrayFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: Vec<NaiveDate>) -> Value {
|
||||||
|
Value::DateArray(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_date_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_date_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> 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<DateTimeFormula> {
|
||||||
|
DateTimeFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: NaiveDateTime) -> Value {
|
||||||
|
Value::DateTime(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_datetime()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_datetime()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> EnumValueFormula {
|
||||||
|
EnumValueFormula::DateTime(formula)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyType for DateTimeArrayType {
|
||||||
|
type T = Vec<NaiveDateTime>;
|
||||||
|
const TYPE: Type = Type::DateTimeArray;
|
||||||
|
|
||||||
|
fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult<DateTimeArrayFormula> {
|
||||||
|
DateTimeArrayFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: Vec<NaiveDateTime>) -> Value {
|
||||||
|
Value::DateTimeArray(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_datetime_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_datetime_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> EnumValueFormula {
|
||||||
|
EnumValueFormula::DateTimeArray(formula)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScalarType for DateTimeType {
|
||||||
|
type ARRAY = DateTimeArrayType;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ObjectType;
|
||||||
|
pub struct ObjectArrayType;
|
||||||
|
|
||||||
|
impl AnyType for ObjectType {
|
||||||
|
type T = Arc<dyn FormulaObject>;
|
||||||
|
const TYPE: Type = Type::Object;
|
||||||
|
|
||||||
|
fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult<ObjectFormula> {
|
||||||
|
ObjectFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: Arc<dyn FormulaObject>) -> Value {
|
||||||
|
Value::Object(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_object()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_object()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> EnumValueFormula {
|
||||||
|
EnumValueFormula::Object(formula)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyType for ObjectArrayType {
|
||||||
|
type T = Vec<Arc<dyn FormulaObject>>;
|
||||||
|
const TYPE: Type = Type::ObjectArray;
|
||||||
|
|
||||||
|
fn formula_from_reader(reader: &mut FormulaReader) -> DeserializedResult<ObjectArrayFormula> {
|
||||||
|
ObjectArrayFormulas::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_to_value(value: Vec<Arc<dyn FormulaObject>>) -> Value {
|
||||||
|
Value::ObjectArray(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_value(value: Value) -> Option<Self::T> {
|
||||||
|
value.to_object_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast_from_formula(formula: EnumValueFormula) -> Option<Box<dyn Formula<Self::T>>> {
|
||||||
|
formula.to_object_array()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_enum_formula(formula: Box<dyn Formula<Self::T>>) -> EnumValueFormula {
|
||||||
|
EnumValueFormula::ObjectArray(formula)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScalarType for ObjectType {
|
||||||
|
type ARRAY = ObjectArrayType;
|
||||||
|
}
|
||||||
792
src/value.rs
Normal file
792
src/value.rs
Normal file
@@ -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<dyn FormulaObject>),
|
||||||
|
BoolArray(Vec<bool>),
|
||||||
|
IntArray(Vec<i64>),
|
||||||
|
DecimalArray(Vec<BigDecimal>),
|
||||||
|
StringArray(Vec<String>),
|
||||||
|
DateArray(Vec<NaiveDate>),
|
||||||
|
DateTimeArray(Vec<NaiveDateTime>),
|
||||||
|
ObjectArray(Vec<Arc<dyn FormulaObject>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum EqValue {
|
||||||
|
Bool(bool),
|
||||||
|
Int(i64),
|
||||||
|
Decimal(BigDecimal),
|
||||||
|
String(String),
|
||||||
|
Date(NaiveDate),
|
||||||
|
DateTime(NaiveDateTime),
|
||||||
|
BoolArray(Vec<bool>),
|
||||||
|
IntArray(Vec<i64>),
|
||||||
|
DecimalArray(Vec<BigDecimal>),
|
||||||
|
StringArray(Vec<String>),
|
||||||
|
DateArray(Vec<NaiveDate>),
|
||||||
|
DateTimeArray(Vec<NaiveDateTime>),
|
||||||
|
}
|
||||||
|
|
||||||
|
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<bool> {
|
||||||
|
match self {
|
||||||
|
Value::Bool(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_int(self) -> Option<i64> {
|
||||||
|
match self {
|
||||||
|
Value::Int(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_decimal(self) -> Option<BigDecimal> {
|
||||||
|
match self {
|
||||||
|
Value::Decimal(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
Value::String(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_date(self) -> Option<NaiveDate> {
|
||||||
|
match self {
|
||||||
|
Value::Date(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_datetime(self) -> Option<NaiveDateTime> {
|
||||||
|
match self {
|
||||||
|
Value::DateTime(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_object(self) -> Option<Arc<dyn FormulaObject>> {
|
||||||
|
match self {
|
||||||
|
Value::Object(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bool_array(self) -> Option<Vec<bool>> {
|
||||||
|
match self {
|
||||||
|
Value::BoolArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_int_array(self) -> Option<Vec<i64>> {
|
||||||
|
match self {
|
||||||
|
Value::IntArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_decimal_array(self) -> Option<Vec<BigDecimal>> {
|
||||||
|
match self {
|
||||||
|
Value::DecimalArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string_array(self) -> Option<Vec<String>> {
|
||||||
|
match self {
|
||||||
|
Value::StringArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_date_array(self) -> Option<Vec<NaiveDate>> {
|
||||||
|
match self {
|
||||||
|
Value::DateArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_datetime_array(self) -> Option<Vec<NaiveDateTime>> {
|
||||||
|
match self {
|
||||||
|
Value::DateTimeArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_object_array(self) -> Option<Vec<Arc<dyn FormulaObject>>> {
|
||||||
|
match self {
|
||||||
|
Value::ObjectArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn comparable(self) -> Option<EqValue> {
|
||||||
|
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<Vec<EnumValueFormula>, 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<EnumValueFormula> {
|
||||||
|
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<T: AnyType> {
|
||||||
|
value: Box<dyn Formula<T::T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AnyType> Formula<Value> for BoxValueFormula<T> {
|
||||||
|
fn evaluate(&self, context: &FormulaContext) -> Result<Value, ExecutionError> {
|
||||||
|
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<EnumValueFormula> {
|
||||||
|
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<LocalDefinition>) -> 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<BoolFormula> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::Bool(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_int(self) -> Option<IntFormula> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::Int(value) => Some(value),
|
||||||
|
EnumValueFormula::IntConstant(value) => Some(IntFormulas::value(value)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_int_constant(self) -> Option<i64> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::IntConstant(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_decimal(self) -> Option<DecimalFormula> {
|
||||||
|
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<StringFormula> {
|
||||||
|
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<String> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::StringConstant(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_date(self) -> Option<DateFormula> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::Date(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_datetime(self) -> Option<DateTimeFormula> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::DateTime(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_object(self) -> Option<ObjectFormula> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::Object(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bool_array(self) -> Option<BoolArrayFormula> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::BoolArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_int_array(self) -> Option<IntArrayFormula> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::IntArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_decimal_array(self) -> Option<DecimalArrayFormula> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::DecimalArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string_array(self) -> Option<StringArrayFormula> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::StringArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_date_array(self) -> Option<DateArrayFormula> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::DateArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_datetime_array(self) -> Option<DateTimeArrayFormula> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::DateTimeArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_object_array(self) -> Option<ObjectArrayFormula> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::ObjectArray(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_literal_constant(self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
EnumValueFormula::LiteralConstant(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&self, context: &FormulaContext) -> Result<Value, ExecutionError> {
|
||||||
|
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<u8> {
|
||||||
|
let mut writer = FormulaWriter::new();
|
||||||
|
self.serialize_to(&mut writer);
|
||||||
|
writer.into_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(data: &[u8]) -> DeserializedResult<EnumValueFormula> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user