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