Initial commit

This commit is contained in:
2024-04-17 12:08:08 +02:00
commit 4d9cba3c11
26 changed files with 12352 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

517
Cargo.lock generated Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

1532
src/bool_formula.rs Normal file

File diff suppressed because it is too large Load Diff

1272
src/compilation/compiler.rs Normal file

File diff suppressed because it is too large Load Diff

172
src/compilation/lexer.rs Normal file
View 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
View 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
View 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
View 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());
}

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

88
src/formula_reader.rs Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

185
src/lib.rs Normal file
View 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
View 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
View 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
View 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(&regex_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
View 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
View 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
View 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);
}
}
}
}